Libraries

library(tidyverse)
── Attaching core tidyverse packages ─────────────────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.2     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.4.2     ✔ tibble    3.2.1
✔ lubridate 1.9.2     ✔ tidyr     1.3.0
✔ purrr     1.0.1     ── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
R Version
---               _                           
platform       x86_64-apple-darwin20       
arch           x86_64                      
os             darwin20                    
system         x86_64, darwin20            
status                                     
major          4                           
minor          3.1                         
year           2023                        
month          06                          
day            16                          
svn rev        84548                       
language       R                           
version.string R version 4.3.1 (2023-06-16)
nickname       Beagle Scouts               

Package Versions
---
tidyverse 2.0.0

Settings

models_format <- c(
    "_cghr10" = "",
    "_" = " & ",
    "chatgpt3" = "ChatGPT-3.5",
    "chatgpt4" = "ChatGPT-4",
    "insilicova" = "InSilicoVA",
    "interva5" = "InterVA-5"
)
stage_format <- c(
    "is_" = "",
    "recon" = "Reconciliation",
    "agreed" = "Agreement",
    "adj" = "Adjudication"
)

Functions

Functions for PCCC and CSMF Accuracy.


# Calc PCCC
calc_pccc <- function(actual, pred, k = 1, N = NULL) {
    
    # Calc N num of causes if not known
    N <- if (is.null(N)) length(unique(na.omit(c(actual, pred)))) else N
    
    # Calc frac of deaths in top k causes
    TP <- actual == pred
    TP[is.na(TP) | is.null(TP)] <- FALSE # for no preds
    C <- sum(TP) / length(actual)
    
    # Calc PCCC
    out <- (C - (k/N)) / (1 - (k/N))
    return(out)
}

# Calc CSMF accuracy
calc_csmf_acc <- function(actual, pred) {
    
    # Get all unique causes
    causes <- unique(c(actual, pred))
    
    # Get csmfs
    cases <- length(actual)
    csmf_true <- table(actual) / cases
    csmf_pred <- table(pred) / cases
    
    # Correct for missing causes in either actual or pred
    csmf_true <- vapply(
        causes,
        function(x) if (x %in% names(csmf_true)) csmf_true[x] else 0,
        FUN.VALUE = numeric(1)
    )
    csmf_pred <- vapply(
        causes,
        function(x) if (x %in% names(csmf_pred)) csmf_pred[x] else 0,
        FUN.VALUE = numeric(1)
    )
    
    # Calc csmf max error
    csmf_max_error <- 2 * (1 - min(csmf_true))
    
    # Calc csmf acc
    out <- 1 - (sum(abs(csmf_true - csmf_pred)) / csmf_max_error)
    return(out)
}

# Bulk calc per model
calc_per_model <- function(actual, pred, func, name = "Metric", ...) {
    
    # Prep data in long format grouped by model
    out <- pred %>%
        pivot_longer( # to long format
            everything(),
            names_to = "Model",
            values_to = "prediction"
        ) %>% group_by(Model)
    
    # Calc k if pccc as it is diff for model combos
    if (identical(func, calc_pccc)) {
        out <- out %>%
            summarise(
                !!name := func(
                    actual,
                    prediction,
                    k = str_count(unique(Model), "_"),
                    ...
                )
            )
    } else { # otherwise apply func
        out <- out %>%
            summarise(
                !!name := func(
                    actual,
                    prediction,
                    ...
                )
            )
    }
    
    # Format display and return
    out <- out %>%
        mutate(Model = str_replace_all(Model, models_format)) %>%
        arrange(across({{name}}, desc))
    return(out)
}

# Bulk calc per model based on age, physician coding stage, and cause of death
calc_by <- function(
        df,
        func,
        name = "Metric",
        by_age = TRUE,
        by_stage = TRUE,
        by_sex = TRUE,
        by_cod = TRUE,
        by_age_range = TRUE,
        ...
    ) {
    out <- list()
    
    # Calc overall metric
    out[[name]] <- calc_per_model(
        actual = df %>% pull(physician_cghr10),
        pred = df %>% select(ends_with("_cghr10"), -physician_cghr10),
        func = func,
        ...
    ) %>% rename(
        {{ name }} := Metric
    )
    
    # Calc metric for va coding stage
    if (by_stage) {
        for (stage in c("is_agreed", "is_recon", "is_adj")) {
            
            # Filter data for stage
            df_filter <- df %>% filter(.[[stage]] == TRUE)
            metric_name = paste0(
                name,
                " ",
                str_replace_all(stage, stage_format)
            )
            
            # Calc metric for stage
            out[[metric_name]] <- calc_per_model(
                actual = df_filter %>%
                    pull(physician_cghr10),
                pred = df_filter %>%
                    select(ends_with("_cghr10"), -physician_cghr10),
                func = func,
                ...
            ) %>% rename(
                 {{ metric_name }} := Metric
            )
        }
    }
    
    # Calc pcc for each age group
    if (by_age) {
        for (age_group in c("adult", "child", "neo")) {
            
            # Filter data for age
            df_filter <- df %>% filter(age == age_group)
            metric_name = paste0(
                name,
                " ",
                str_to_title(age_group)
            )
        
            # Calc metric for age
            out[[metric_name]] <- calc_per_model(
                actual = df_filter %>%
                    pull(physician_cghr10),
                pred = df_filter %>%
                    select(ends_with("_cghr10"), -physician_cghr10),
                func = func,
                ...
            ) %>% rename(
                 {{ metric_name }} := Metric
            )
            
            # Calc metric for each stage within age group
            for (stage in c("is_agreed", "is_recon", "is_adj")) {
            
                # Filter data for stage within age group
                df_filter <- df %>% filter(
                    .[[stage]] == TRUE & age == age_group
                )
                metric_name <- paste0(
                    name,
                    " ",
                    str_to_title(age_group),
                    " ",
                    str_replace_all(stage, stage_format)
                )
                
                # Calc metric for stage within age group
                out[[metric_name]] <- calc_per_model(
                    actual = df_filter %>%
                        pull(physician_cghr10),
                    pred = df_filter %>%
                        select(ends_with("_cghr10"), -physician_cghr10),
                    func = func,
                    ...
                ) %>% rename(
                     {{ metric_name }} := Metric
                )
            }
        }
    }
    
    # Calc metric by sex for each age group
    if (by_sex) {
        
        # Calc metric by cod
        for (a in c("adult", "child", "neo")) {
            for (sx in c("Male", "Female")) {
                
                # Filter data for cod
                df_filter <- df %>% filter(
                    sex == sx & is_agreed == TRUE & age == a
                )
                metric_name <- sprintf(
                    "%s %s Sex Agree %s", name, str_to_title(a), sx
                )
                
                # Calc metric for cod
                if (nrow(df_filter) > 0) {
                    out[[metric_name]] <- calc_per_model(
                        actual = df_filter %>%
                            pull(physician_cghr10),
                        pred = df_filter %>%
                            select(ends_with("_cghr10"), -physician_cghr10),
                        func = func,
                        ...
                    ) %>% rename(
                         {{ metric_name }} := Metric
                    )
                }
            }
        }
    }
    
    # Calc metric by cause of death for each age group
    if (by_cod) {
        
        # Get unique causes of death
        causes <- df %>%
            select(ends_with("_cghr10")) %>%
            pivot_longer(
                everything(),
                names_to = "column",
                values_to = "cod"
            ) %>%
            distinct(cod) %>%
            filter(!is.na(cod)) %>%
            pull(cod)
        
        # Calc metric by cod
        for (a in c("adult", "child", "neo")) {
            for (cod in causes) {
                
                # Filter data for cod
                df_filter <- df %>% filter(
                    physician_cghr10 == cod & is_agreed == TRUE & age == a
                )
                metric_name <- sprintf(
                    "%s %s COD Agree %s", name, str_to_title(a), cod
                )
                
                # Calc metric for cod
                if (nrow(df_filter) > 0) {
                    out[[metric_name]] <- calc_per_model(
                        actual = df_filter %>%
                            pull(physician_cghr10),
                        pred = df_filter %>%
                            select(ends_with("_cghr10"), -physician_cghr10),
                        func = func,
                        ...
                    ) %>% rename(
                         {{ metric_name }} := Metric
                    )
                }
            }
        }
    }
    
    # Calc metric by age range
    if (by_age_range) {
        
        # Get unique age ranges
        age_ranges <- df %>%
            pull(age_range) %>%
            unique
        
        # Calc metric by age range
        for (a in c("adult", "child", "neo")) {
            for (ar in age_ranges) {
                
                # Filter for age range
                df_filter <- df %>% filter(
                    age_range == ar & is_agreed == TRUE & age == a
                )
                metric_name <- sprintf(
                    "%s %s Age Agree %s", name, str_to_title(a), ar
                )
                
                # Calc metric for cod
                if (nrow(df_filter) > 0) {
                    out[[metric_name]] <- calc_per_model(
                        actual = df_filter %>%
                            pull(physician_cghr10),
                        pred = df_filter %>%
                            select(ends_with("_cghr10"), -physician_cghr10),
                        func = func,
                        ...
                    ) %>% rename(
                         {{ metric_name }} := Metric
                    )
                }
            }
        }
    }
    
    # Combine all pccc metrics
    out <- reduce(out,
        function(x, y) left_join(x, y, by = "Model")
    ) %>%
        arrange(across({{ name }}, desc))
    return(out)
}

Data

Raw Data

Load data from data folder.

raw_df <- read_csv("../data/healsl_rd1to2_cod_v1.csv")
Cases:  11920 

Clean Labels

Clean age_range labels to include only values and unit of measure in titlecase.

df <- raw_df %>% mutate(
    "age_range" = str_to_title(str_replace(
        age_range,
        "\\s*\\(.*\\)",
        ""
    )),
    "age_range" = if_else(
        !str_detect(age_range, "Year|Years|Day|Days|month|Months|week|Weeks"),
        paste0(age_range, " Years"),
        age_range
    )
)
df

Physician Cases

Filter for cases that were coded by physicians.

df <- df %>%
    filter(!is.na(physician_cghr10) & !is.null(physician_cghr10))
Physician Coded Cases:  11799

Combined Models

Create model combinations where if any of the models have the physician code, then set the combined model’s output for the case to be the physician code.

df <- df %>%
    mutate( # Combine models
        "chatgpt3_insilicova_cghr10" = if_else(
            chatgpt3_cghr10 == physician_cghr10 | 
                insilicova_cghr10 == physician_cghr10,
            physician_cghr10,
            chatgpt3_cghr10
        ),
        "chatgpt4_insilicova_cghr10" = if_else(
            chatgpt4_cghr10 == physician_cghr10 | 
                insilicova_cghr10 == physician_cghr10,
            physician_cghr10,
            chatgpt4_cghr10
        ),
        "chatgpt3_interva5_cghr10" = if_else(
            chatgpt3_cghr10 == physician_cghr10 | 
                interva5_cghr10 == physician_cghr10,
            physician_cghr10,
            chatgpt3_cghr10
        ),
        "chatgpt4_interva5_cghr10" = if_else(
            chatgpt4_cghr10 == physician_cghr10 | 
                interva5_cghr10 == physician_cghr10,
            physician_cghr10,
            chatgpt4_cghr10
        ),
        "chatgpt3_insilicova_interva5_cghr10" = if_else(
            chatgpt3_cghr10 == physician_cghr10 | 
                insilicova_cghr10 == physician_cghr10 |
                interva5_cghr10 == physician_cghr10,
            physician_cghr10,
            chatgpt3_cghr10
        ),
        "chatgpt4_insilicova_interva5_cghr10" = if_else(
            chatgpt4_cghr10 == physician_cghr10 | 
                insilicova_cghr10 == physician_cghr10 |
                interva5_cghr10 == physician_cghr10,
            physician_cghr10,
            chatgpt4_cghr10
        )
    )

Prepared Data

Display case counts for raw data and prepared data after combining models and filtering for physician coded cases.


cat("\nRaw Data\n--------\n\n")

Raw Data
--------
# Physician info
raw_physicians <- raw_df$physician_cghr10
cat(paste0(
    "Cases (",
    length(unique(na.omit(raw_physicians))), " CODs): ",
    length(raw_physicians),
    "\n"
))
Cases (29 CODs): 11920
# Models cases
raw_models <- raw_df %>% select(ends_with("_cghr10"), -physician_cghr10)
for (mcol in colnames(raw_models)) {
    m <- raw_models[[mcol]]
    cat(paste0(
        str_replace_all(mcol, models_format), " Predicted Cases (", 
        length(unique(na.omit(m))), " CODs): ",
        length(m) - sum(is.na(m) | is.null(m)),
        "\n"
    ))
}
ChatGPT-3.5 Predicted Cases (29 CODs): 11785
ChatGPT-4 Predicted Cases (29 CODs): 11703
InSilicoVA Predicted Cases (29 CODs): 11548
InterVA-5 Predicted Cases (28 CODs): 11665
cat("\nPrepared Data\n--------------\n")

Prepared Data
--------------
# Physician cases
physicians <- df$physician_cghr10
cat(paste0(
    "Physician Coded Cases (",
    length(unique(na.omit(physicians))), " CODs): ",
    length(physicians) - sum(is.na(physicians) | is.null(physicians)),
    "\n"
))
Physician Coded Cases (29 CODs): 11799
# Models cases
models <- df %>% select(ends_with("_cghr10"), -physician_cghr10)
for (mcol in colnames(models)) {
    m <- models[[mcol]]
    cat(paste0(
        str_replace_all(mcol, models_format), " Predicted Cases (", 
        length(unique(na.omit(m))), " CODs): ",
        length(m) - sum(is.na(m) | is.null(m)),
        "\n"
    ))
}
ChatGPT-3.5 Predicted Cases (29 CODs): 11707
ChatGPT-4 Predicted Cases (29 CODs): 11628
InSilicoVA Predicted Cases (29 CODs): 11460
InterVA-5 Predicted Cases (28 CODs): 11575
ChatGPT-3.5 & InSilicoVA Predicted Cases (29 CODs): 11598
ChatGPT-4 & InSilicoVA Predicted Cases (29 CODs): 11550
ChatGPT-3.5 & InterVA-5 Predicted Cases (29 CODs): 11639
ChatGPT-4 & InterVA-5 Predicted Cases (29 CODs): 11577
ChatGPT-3.5 & InSilicoVA & InterVA-5 Predicted Cases (29 CODs): 11618
ChatGPT-4 & InSilicoVA & InterVA-5 Predicted Cases (29 CODs): 11572

Metrics

Calculate metrics from model outputs compared to physician codes.

metrics <- list()

PCCC

Calculate Partial Chance Corrected Concordance (PCCC) to evaluate indivudal performance for each model by age and physician coding stage.


# Get num of unique causes
ncauses <- df %>%
    select(ends_with("_cghr10")) %>%
    pivot_longer(
        everything(),
        names_to = "column",
        values_to = "cod"
    ) %>%
    distinct(cod) %>%
    filter(!is.na(cod)) %>%
    pull(cod) %>%
    length

# Calc pccc for age, stage, and cod
metrics$pccc <- calc_by(df, calc_pccc, "PCCC", N = ncauses)
metrics$pccc

CSMF Accuracy

Calculate Cause Specific Mortality Fraction (CSMF) Accuracy to evaluate population performance for each model by age and physician coding stage.

metrics$csmf_acc <- calc_by(
    df,
    calc_csmf_acc,
    "CSMF Accuracy",
    by_cod = FALSE
)
metrics$csmf_acc

Plots

Save calculated metrics to data folder.

out <- reduce(metrics,
              function(x, y) left_join(x, y, by = "Model")
)
write_csv(out, "../data/healsl_rd1to2_metrics_v1.csv")

Exploration


# Num and perc of agreed cases
nall <- nrow(df)
nagree <- nrow(df %>% filter(is_agreed == TRUE))
pagree <- (nagree / nall) * 100

# Create labels to axis titles
y_title <- paste0("Physician Agreed Records (n=", nagree, ", 100%)")

Cases by Sex and Age Group

Display a plot of cases by age group separated by sex.


# Calc age and sex counts
nadult <- nrow(df %>% filter(age == "adult" & is_agreed == TRUE))
nchild <- nrow(df %>% filter(age == "child" & is_agreed == TRUE))
nneo <- nrow(df %>% filter(age == "neo" & is_agreed == TRUE))

# Calc age and sex perc
padult <- (nadult / nagree) * 100
pchild <- (nchild / nagree) * 100
pneo <- (nneo / nagree) * 100

# Create age with counts
age_remap <- c(
    "Adult" = paste0("Adult, 12+ years\n(n=", nadult, ", ", round(padult), "%)"),
    "Child" = paste0("Child, 28 days to 11 years\n(n=", nchild, ", ", round(pchild), "%)"),
    "Neo" = paste0("Neonatal, <28 days\n(n=", nneo, ", ", round(pneo), "%)")
)

# Format the data for plotting
asdata <- df %>%
    filter(is_agreed == TRUE) %>%
    group_by(age, sex) %>%
    summarize(count = n()) %>%
    mutate(
        age = str_to_title(age),
        sex = paste0(
            sex, " (n=", count, ", ",
            round(count / case_when(
                age == "Adult" ~ nadult,
                age == "Child" ~ nchild,
                age == "Neo" ~ nneo
            ) * 100), "%)"
        )
    ) %>%
    mutate( # re-order age
        age = factor(age, levels = c(
            "Adult",
            "Child",
            "Neo"
        ))
    ) %>%
    mutate( # rename ages with counts
        age = recode(age, !!!age_remap)
    )
`summarise()` has grouped output by 'age'. You can override using the `.groups` argument.
# Plot the data
asplot <- asdata %>%
    ggplot(aes(x = factor(age), y = count, fill = sex)) +
    geom_bar(
        stat = "identity",
        fill = "white",
        color = "black"
    ) +
    geom_text(
        aes(label = sex),
        position = position_stack(vjust = 0.5),
        size = 2.5,
        color = "black"
    ) +
    labs(
        y = y_title,
        x = element_blank(),
        fill = "Sex"
    ) +
    theme_minimal() +
    theme(
        panel.grid = element_blank(),
        axis.title.x = element_text(margin = margin(t = 10)),
        axis.title.y = element_text(margin = margin(r = 10))
    )

# Save the plot
asplot
ggsave("../manuscript/fig-data-agesex.pdf", plot = asplot, dpi = 300)
Saving 7.29 x 4.51 in image

Cases by Age Range

Display a plot for each age group of cases by age range.

for (a in c("adult", "child", "neo")) {
    
    # Create label for age group
    arows <- df %>% filter(is_agreed == TRUE & age == a) %>% nrow
    alabel <- if (a == "neo") "Neonatal" else str_to_title(a)
    atitle <- paste0("Physician Agreed ", alabel, " Records (n=", arows, ", 100%)")
    
    # Format plot data
    eardata <- df %>%
        filter(is_agreed == TRUE & age == a) %>%
        group_by(age_range) %>%
        summarize(count = n()) %>%
        separate(
            age_range,
            into = c("age_range_value", "age_range_unit"),
            sep = " "
        ) %>%
        separate(
            age_range_value,
            into = c("age_range_min", "age_range_max"),
            sep = "-"
        ) %>%
        mutate(
            age_range_min = as.integer(age_range_min),
            age_range_max = as.integer(age_range_max),
            age_range_unit = factor(age_range_unit, levels = c(
                "Weeks",
                "Days",
                "Months",
                "Years"
            )),
            age_range_label = paste0(
                age_range_min,
                "-",
                age_range_max,
                " ",
                age_range_unit,
                "\n(n=", count, ", ", round((count / arows) * 100), "%)"
            )
        ) %>%
        arrange(age_range_unit, age_range_min, age_range_max) %>%
        mutate(
            age_range_label = factor(age_range_label, levels = unique(age_range_label))
        )
    
    # Plot the data
    earplot <- eardata %>%
        ggplot(aes(x = age_range_label, y = count)) +
        geom_bar(
            stat = "identity",
            fill = "#1d1d1d",
            color = "white",
            width = 0.5
        ) +
        labs(
            y = atitle,
            x = element_blank()
        ) +
        theme_minimal() +
        theme(
            panel.grid = element_blank(),
            axis.title.x = element_text(margin = margin(t = 10)),
            axis.title.y = element_text(margin = margin(r = 10))
        ) +
        coord_flip()
    
    # Save the plot
    print(earplot)
    ggsave(
        sprintf("../manuscript/fig-data-agerange-%s.pdf", a),
        plot = earplot,
        dpi = 300
    )
}

Cases by COD

Display a plot for each age group of cases by cause of death.

for (a in c("adult", "child", "neo")) {
    
    # Create label for age group
    arows <- df %>% filter(is_agreed == TRUE & age == a) %>% nrow
    alabel <- if (a == "neo") "Neonatal" else str_to_title(a)
    atitle <- paste0("Physician Agreed ", alabel, " Records (n=", arows, ", 100%)")
    
    # Format plot data
    ecoddata <- df %>%
        filter(is_agreed == TRUE & age == a) %>%
        group_by(physician_cghr10) %>%
        summarize(count = n()) %>%
        mutate(
            cod_label = paste0(
                if_else(
                    str_count(physician_cghr10, "\\s+") > 3,
                    str_replace(physician_cghr10, "(\\S+\\s+\\S+\\s+\\S+) ", "\\1\n"),
                    physician_cghr10
                ),
                "\n(n=", count, ", ",
                if_else(
                    ((count / arows) * 100) < 1,
                    "<1",
                    as.character(round((count / arows) * 100))
                ), "%)"
            )
        ) %>%
        arrange(count) %>%
        mutate(
            cod_label = factor(cod_label, levels = unique(cod_label))
        )
    
    # Plot the data
    ecodplot <- ecoddata %>%
        ggplot(aes(x = cod_label, y = count)) +
        geom_bar(
            stat = "identity",
            fill = "#1d1d1d",
            color = "white",
            width = 0.5
        ) +
        labs(
            y = atitle,
            x = element_blank()
        ) +
        theme_minimal() +
        theme(
            panel.grid = element_blank(),
            axis.title.x = element_text(margin = margin(t = 10)),
            axis.title.y = element_text(margin = margin(r = 10)),
            axis.text.x = element_text(hjust = 1)
        ) +
        coord_flip()
    
    # Save the plot
    print(ecodplot)
    ggsave(
        sprintf("../manuscript/fig-data-cod-%s.pdf", a),
        plot = ecodplot,
        dpi = 300,
        width = if (a == "adult") 6 else NA,
        height = if (a == "adult") 8 else NA
    )
}

Performance

All vs Agree Performance

Display a plot of model performance for all records versus records where physicians agreed on the COD code.


# Calc stage counts
nall <- nrow(df)
nagree <- nrow(df %>% filter(is_agreed == TRUE))
nrecon <- nrow(df %>% filter(is_recon == TRUE))
nadj <- nrow(df %>% filter(is_adj == TRUE))

# Calc stage perc
pagree <- (nagree / nall) * 100
precon <- (nrecon / nall) * 100
padj <- (nadj / nall) * 100

# Create stage with counts
stage_remap <- c(
    "All" = paste0("All\nRecords\n(n=", nall, ", 100%)"),
    "Agreed" = paste0("Physician Agreed\nRecords\n(n=", nagree, ", ", round(pagree), "%)"),
    "Reconciled" = paste0("Reconciled\n(n=", nrecon, ", ", round(precon), "%)"),
    "Adjudicated" = paste0("Adjudicated\n(n=", nadj, ", ", round(padj), "%)")
)

# Prepare boxplot data
alldata <- out %>%
    select(
        Model,
        `PCCC`,
        `PCCC Agreement`,
        `CSMF Accuracy`,
        `CSMF Accuracy Agreement`
    ) %>%
    rename(
         All = `PCCC`,
         Agreed = `PCCC Agreement`
    ) %>%
    filter(!str_detect(Model, "&")) %>% # remove multi models
    pivot_longer( # transform to long format
        cols = -c(
            Model,
            `CSMF Accuracy`,
            `CSMF Accuracy Agreement`
        ),
        names_to = "Stage",
        values_to = "PCCC"
    ) %>%
    group_by(Stage) %>%
    mutate( # Add min, mid, and max model names to label
        "PCCC Min" = if_else(
            PCCC <= min(PCCC) + 0.05,
            paste0(
                Model, "\n(",
                if_else(round(PCCC, 2) != round(min(PCCC), 2), paste0(round(PCCC, 2), ", "), ""),
                "CSMF=", round(if_else(Stage == "All", `CSMF Accuracy`, `CSMF Accuracy Agreement`), 2), ")"),
            NA
        ),
        "PCCC Max" = if_else(
            PCCC >= max(PCCC) - 0.05,
            paste0(
                Model, "\n(",
                if_else(round(PCCC, 2) != round(max(PCCC), 2), paste0(round(PCCC, 2), ", "), ""),
                "CSMF=", round(if_else(Stage == "All", `CSMF Accuracy`, `CSMF Accuracy Agreement`), 2), ")"),
            NA
        ),
        "PCCC Mid" = if_else(
            is.na(`PCCC Min`) & is.na(`PCCC Max`),
            paste0(Model, "\n(", round(PCCC, 2), ", CSMF=", round(if_else(Stage == "All", `CSMF Accuracy`, `CSMF Accuracy Agreement`), 2), ")"),
            NA
        ),
        "PCCC Mid Value" = if_else(
            is.na(`PCCC Min`) & is.na(`PCCC Max`),
            PCCC,
            NA
        ),
        "PCCC Min" = if_else( # Combine into one row if close PCCC
            `PCCC` == min(PCCC),
            if_else(
                sum(!is.na(`PCCC Min`)) > 1,
                paste0(na.omit(`PCCC Min`), collapse = "\n"),
                paste0(
                    str_replace(na.omit(`PCCC Min`), "\\s*\\(.*\\,\\s*", "\n("),
                    collapse = "\n"
                )
            ),
            NA
        ),
        "PCCC Max" = if_else(
            `PCCC` == max(PCCC),
            if_else(
                sum(!is.na(`PCCC Max`)) > 1,
                paste0(na.omit(`PCCC Max`), collapse = "\n"),
                paste0(
                    str_replace(na.omit(`PCCC Max`), "\\s*\\(.*\\,\\s*", "\n("),
                    collapse = "\n"
                )
            ),
            NA
        )
    ) %>%
    mutate( # re-order stage
        Stage = factor(Stage, levels = c(
            "Agreed",
            "All"
        ))
    ) %>%
    mutate( # rename stages with counts
        Stage = recode(Stage, !!!stage_remap)
    )

# Plot boxplot
allplot <- alldata %>%
    ggplot(aes(x = Stage, y = PCCC)) +
    geom_boxplot(
        linewidth = 0.5,
        width = 0.25
    ) +
    geom_point(
        aes(y = `PCCC Mid Value`),
        shape = 1,
        size = 1.5,
        fill = "white",
        color = "darkgray",
        alpha = 0.8,
        position = position_nudge(x = -0.3)
    ) +
    geom_text(
        aes(label = `PCCC Min`),
        color = "#4d4d4d",
        size = 2,
        hjust = 1,
        position = position_nudge(y = -0.055)
    ) +
    geom_text(
        aes(label = `PCCC Max`),
        color = "#4d4d4d",
        size = 2,
        hjust = 0,
        position = position_nudge(y = 0.055)
    ) +
    geom_text(
        aes(label = `PCCC Mid`),
        color = "#4d4d4d",
        size = 2,
        hjust = 1,
        position = position_nudge(y = -0.015, x = -0.3)
    ) +
    stat_summary( # min pccc txt on boxplot
        geom = "text",
        fun = min,
        aes(label = sprintf("%1.2f", after_stat(y))),
        position = position_nudge(y = -0.025),
        size = 3
    ) +
    stat_summary( # max pccc txt on boxplot
        geom = "text",
        fun = max,
        aes(label = sprintf("%1.2f", after_stat(y))),
        position = position_nudge(y = 0.025),
        size = 3
    ) +
    labs(
        x = element_blank(),
        y = "PCCC (0=Low, 1=High)"
    ) +
    ylim(0.2, 0.85) +
    coord_flip() +
    theme_minimal() +
    theme(
        panel.grid = element_blank(),
        axis.line = element_line(color = "black"),
        axis.title.x = element_text(margin = margin(t = 10)),
        axis.title.y = element_text(margin = margin(r = 10)),
        axis.line.x = element_line(
            arrow = grid::arrow(length = unit(0.2, "cm"), ends = "both")
        ),
        axis.line.y = element_blank()
    )

# Save the plot
allplot

ggsave("../manuscript/fig-perf-allvsagree.pdf", plot = allplot, dpi = 300)
Saving 6 x 2.5 in image

Age Group Perfomance

Display a plot of performance for physician agreed records by age group.


# Calc age counts
nagree <- nrow(df %>% filter(is_agreed == TRUE))
nadult <- nrow(df %>% filter(age == "adult" & is_agreed == TRUE))
nchild <- nrow(df %>% filter(age == "child" & is_agreed == TRUE))
nneo <- nrow(df %>% filter(age == "neo" & is_agreed == TRUE))

# Calc age perc
padult <- (nadult / nagree) * 100
pchild <- (nchild / nagree) * 100
pneo <- (nneo / nagree) * 100

# Create age with counts
age_remap <- c(
    "Adult" = paste0("Adult\n12+ years\n(n=", nadult, ", ", round(padult), "%)"),
    "Child" = paste0("Child\n28 days to 11 years\n(n=", nchild, ", ", round(pchild), "%)"),
    "Neonatal" = paste0("Neonatal\n<28 days\n(n=", nneo, ", ", round(pneo), "%)")
)

# Prepare boxplot data
agedata <- out %>%
    select(
        Model,
        `PCCC Adult Agreement`,
        `PCCC Child Agreement`,
        `PCCC Neo Agreement`,
        `CSMF Accuracy Adult Agreement`,
        `CSMF Accuracy Child Agreement`,
        `CSMF Accuracy Neo Agreement`
    ) %>%
    rename(
         Adult = `PCCC Adult Agreement`,
         Child = `PCCC Child Agreement`,
         Neonatal = `PCCC Neo Agreement`
    ) %>%
    filter(!str_detect(Model, "&")) %>% # remove multi models
    pivot_longer( # transform to long format
        cols = -c(
            Model,
            `CSMF Accuracy Adult Agreement`,
            `CSMF Accuracy Child Agreement`,
            `CSMF Accuracy Neo Agreement`
        ),
        names_to = "Age Group",
        values_to = "PCCC"
    ) %>%
    group_by(`Age Group`) %>%
    mutate( # Add min, mid, and max model names to label
        "PCCC Min" = if_else(
            PCCC <= min(PCCC) + 0.05,
            paste0(
                Model, "\n(",
                if_else(round(PCCC, 2) != round(min(PCCC), 2), paste0(round(PCCC, 2), ", "), ""),
                "CSMF=", round(case_when(
                    `Age Group` == "Adult"  ~ `CSMF Accuracy Adult Agreement`,
                    `Age Group` == "Child"  ~ `CSMF Accuracy Child Agreement`,
                    `Age Group` == "Neonatal"  ~ `CSMF Accuracy Neo Agreement`
                ),2), ")"),
            NA
        ),
        "PCCC Max" = if_else(
            PCCC >= max(PCCC) - 0.05,
            paste0(
                Model, "\n(",
                if_else(round(PCCC, 2) != round(max(PCCC), 2), paste0(round(PCCC, 2), ", "), ""),
                "CSMF=", round(case_when(
                    `Age Group` == "Adult"  ~ `CSMF Accuracy Adult Agreement`,
                    `Age Group` == "Child"  ~ `CSMF Accuracy Child Agreement`,
                    `Age Group` == "Neonatal"  ~ `CSMF Accuracy Neo Agreement`
                ),2), ")"),
            NA
        ),
        "PCCC Mid" = if_else(
            is.na(`PCCC Min`) & is.na(`PCCC Max`),
            paste0(
                Model, "\n(", round(PCCC, 2),
                ", CSMF=", round(case_when(
                    `Age Group` == "Adult"  ~ `CSMF Accuracy Adult Agreement`,
                    `Age Group` == "Child"  ~ `CSMF Accuracy Child Agreement`,
                    `Age Group` == "Neonatal"  ~ `CSMF Accuracy Neo Agreement`
                ),2), ")"),
            NA
        ),
        "PCCC Mid Value" = if_else(
            is.na(`PCCC Min`) & is.na(`PCCC Max`),
            PCCC,
            NA
        ),
        "PCCC Min" = if_else( # Combine into one row if close PCCC
            `PCCC` == min(PCCC),
            if_else(
                sum(!is.na(`PCCC Min`)) > 1,
                paste0(na.omit(`PCCC Min`), collapse = "\n"),
                paste0(
                    str_replace(na.omit(`PCCC Min`), "\\s*\\(.*\\,\\s*", "\n("),
                    collapse = "\n"
                )
            ),
            NA
        ),
        "PCCC Max" = if_else(
            `PCCC` == max(PCCC),
            if_else(
                sum(!is.na(`PCCC Max`)) > 1,
                paste0(na.omit(`PCCC Max`), collapse = "\n"),
                paste0(
                    str_replace(na.omit(`PCCC Max`), "\\s*\\(.*\\,\\s*", "\n("),
                    collapse = "\n"
                )
            ),
            NA
        )
    ) %>%
    mutate( # re-order age
        "Age Group" = factor(`Age Group`, levels = c(
            "Neonatal",
            "Child",
            "Adult"
        ))
    ) %>%
    mutate( # rename ages with counts
        "Age Group" = recode(`Age Group`, !!!age_remap)
    )

# Plot boxplot
ageplot <- agedata %>%
    ggplot(aes(x = `Age Group`, y = PCCC)) +
    geom_boxplot(
        linewidth = 0.5,
        width = 0.25
    ) +
    geom_point(
        aes(y = `PCCC Mid Value`),
        shape = 1,
        size = 1.5,
        fill = "white",
        color = "darkgray",
        alpha = 0.8,
        position = position_nudge(x = -0.3)
    ) +
    geom_text(
        aes(label = `PCCC Min`),
        color = "#4d4d4d",
        size = 2,
        hjust = 1,
        position = position_nudge(y = -0.055)
    ) +
    geom_text(
        aes(label = `PCCC Max`),
        color = "#4d4d4d",
        size = 2,
        hjust = 0,
        position = position_nudge(y = 0.055)
    ) +
    geom_text(
        aes(label = `PCCC Mid`),
        color = "#4d4d4d",
        size = 2,
        hjust = 1,
        position = position_nudge(y = -0.015, x = -0.3)
    ) +
    stat_summary( # min pccc txt on boxplot
        geom = "text",
        fun = min,
        aes(label = sprintf("%1.2f", after_stat(y))),
        position = position_nudge(y = -0.025),
        size = 3
    ) +
    stat_summary( # max pccc txt on boxplot
        geom = "text",
        fun = max,
        aes(label = sprintf("%1.2f", after_stat(y))),
        position = position_nudge(y = 0.025),
        size = 3
    ) +
    labs(
        x = paste0("Physician Agreed Records (n=", nagree, ", 100%)"),
        y = "PCCC (0=Low, 1=High)"
    ) +
    ylim(0.25, 0.9) +
    coord_flip() +
    theme_minimal() +
    theme(
        panel.grid = element_blank(),
        plot.margin = margin(t = 16, b = 12, l = 12, r = 12),
        axis.line = element_line(color = "black"),
        axis.title.x = element_text(margin = margin(t = 10)),
        axis.title.y = element_text(margin = margin(r = 10)),
        axis.line.x = element_line(
            arrow = grid::arrow(length = unit(0.2, "cm"), ends = "both")
        ),
        axis.line.y = element_blank()
    )

# Save the plot
ageplot

ggsave("../manuscript/fig-perf-agegroup.pdf", plot = ageplot, dpi = 300)
Saving 6 x 3.5 in image

Sex Performance

Display a plot for each age group of performance for physician agreed records by sex.

for (a in c("adult", "child", "neo")) {
    
    # Create label and ref for age group
    arows <- df %>% filter(is_agreed == TRUE & age == a) %>% nrow
    alabel <- if (a == "neo") "Neonatal" else str_to_title(a)
    atitle <- paste0("Physician Agreed\n", alabel, " Records\n(n=", arows, ", 100%)")
    aref <- str_to_title(a)
    
    # Get unique sex columns
    sex_col <- out %>% select(starts_with(sprintf(
            "PCCC %s Sex Agree ",
            aref
        ))) %>%
        names
    
    # Create sex with counts
    sex_remap <- list()
    sex_counts <- list()
    for (sx_col in sex_col) {
        
        # Get sex without prefix
        sx <- gsub(sprintf("PCCC %s Sex Agree ", aref), "", sx_col)
        
        # Calc num and perc cases for age range
        nsex <- df %>% filter(sex == sx & is_agreed == TRUE & age == a) %>% nrow
        psex <- (nsex / arows) * 100
        psex_label <- if (round(psex) >= 1) round(psex) else "<1"
        
        # Add label for age ranges
        sex_remap[[sx]] <- paste0(
            sprintf("%s %s", alabel, sx),
            "\n(n=", nsex, ", ", psex_label, "%)"
        )
        
        # Store age range count data
        sex_counts[[sx]] <- nsex
    }
    
    # Prepare boxplot data
    sxdata <- out %>%
        select(
            Model,
            starts_with(sprintf("PCCC %s Sex Agree", aref))
        ) %>%
        rename_at(
             vars(-Model),
             ~gsub(sprintf("PCCC %s Sex Agree ", aref), "", .)
        ) %>%
        filter(!str_detect(Model, "&")) %>% # remove multi models
        pivot_longer( # transform to long format
            cols = -Model,
            names_to = "Sex",
            values_to = "PCCC"
        ) %>%
        mutate( # rename cod with counts
            "Sex" = recode(Sex, !!!sex_remap)
        ) %>%
        group_by(Sex) %>%
        mutate( # Make values less than 0 equal to 0
            PCCC = if_else(PCCC <= 0, 0, PCCC)
        ) %>%
        mutate( # Add min, mid, and max model names to label
            "PCCC Min" = if_else(
                PCCC <= min(PCCC) + 0.05,
                paste0(
                    Model,
                    if_else(round(PCCC, 2) != round(min(PCCC), 2), paste0(" (", round(PCCC, 2), ")"), "")
                ),
                NA
            ),
            "PCCC Max" = if_else(
                PCCC >= max(PCCC) - 0.05,
                paste0(
                    Model,
                    if_else(round(PCCC, 2) != round(max(PCCC), 2), paste0(" (", round(PCCC, 2), ")"), "")
                ),
                NA
            ),
            "PCCC Mid" = if_else(
                is.na(`PCCC Min`) & is.na(`PCCC Max`),
                paste0(Model, "\n(", round(PCCC, 2), ")"),
                NA
            ),
            "PCCC Mid Value" = if_else(
                is.na(`PCCC Min`) & is.na(`PCCC Max`),
                PCCC,
                NA
            ),
            "PCCC Min" = if_else( # Combine into one row if close PCCC
                `PCCC` == min(PCCC),
                if_else(
                    sum(!is.na(`PCCC Min`)) > 1,
                    paste0(na.omit(`PCCC Min`), collapse = "\n"),
                    paste0(
                        str_replace(na.omit(`PCCC Min`), "\\s*\\(.*\\)", ""),
                        collapse = "\n"
                    )
                ),
                NA
            ),
            "PCCC Max" = if_else(
                `PCCC` == max(PCCC),
                if_else(
                    sum(!is.na(`PCCC Max`)) > 1,
                    paste0(na.omit(`PCCC Max`), collapse = "\n"),
                    paste0(str_replace(na.omit(`PCCC Max`), "\\s*\\(.*\\)", ""),
                           collapse = "\n")
                ),
                NA
            )
        )
    
    # Create cod order based on max pccc
    sxorder <- sxdata %>%
        group_by(Sex) %>%
        summarise("PCCC Max Value" = max(PCCC, na.rm = TRUE)) %>%
        select(Sex, `PCCC Max Value`) %>%
        arrange(desc(`PCCC Max Value`)) %>%
        pull(Sex)
    sxdata$Sex <- factor(sxdata$Sex, levels = rev(sxorder))
    
    # Plot boxplot
    sxplot <- sxdata %>%
        ggplot(aes(x = Sex, y = PCCC)) +
        geom_boxplot(
            linewidth = 0.5,
            width = 0.25
        ) +
        geom_point(
            aes(y = `PCCC Mid Value`),
            shape = 1,
            size = 1.5,
            fill = "white",
            color = "darkgray",
            alpha = 0.8,
            position = position_nudge(x = -0.35)
        ) +
        geom_text(
            aes(label = `PCCC Min`),
            color = "#4d4d4d",
            size = 2.5,
            hjust = 1,
            position = position_nudge(y = -0.09)
        ) +
        geom_text(
            aes(label = `PCCC Mid`),
            color = "#4d4d4d",
            size = 2,
            hjust = 1,
            position = position_nudge(y = -0.025, x = -0.35)
        ) +
        geom_text(
            aes(label = `PCCC Max`),
            color = "#4d4d4d",
            size = 2.5,
            hjust = 0,
            position = position_nudge(y = 0.09)
        ) +
        stat_summary( # min pccc txt on boxplot
            geom = "text",
            fun = min,
            aes(label = sprintf("%1.2f", after_stat(y))),
            position = position_nudge(y = -0.04),
            size = 3
        ) +
        stat_summary( # max pccc txt on boxplot
            geom = "text",
            fun = max,
            aes(label = sprintf("%1.2f", after_stat(y))),
            position = position_nudge(y = 0.04),
            size = 3
        ) +
        labs(
            x = atitle,
            y = "PCCC (0=Low, 1=High)"
        ) +
        ylim(0.1, 1) +
        coord_flip() +
        theme_minimal() +
        theme(
            panel.grid = element_blank(),
            axis.line = element_blank(),
            axis.line.y = element_blank(),
            axis.title.x = element_text(margin = margin(t = 10)),
            axis.title.y = element_text(margin = margin(r = 10)),
            axis.line.x = element_line(
                color = "black",
                arrow = grid::arrow(length = unit(0.2, "cm"), ends = "both")
            )
        )
    
    # Save the plot
    print(sxplot)
    ggsave(
        sprintf("../manuscript/fig-perf-sex-%s.pdf", a),
        plot = sxplot,
        dpi = 300
    )
}

Age Range Performance

Display a plot for each age group of performance for physician agreed records by age ranges.

for (a in c("adult", "child", "neo")) {
    
    # Create label and ref for age group
    arows <- df %>% filter(is_agreed == TRUE & age == a) %>% nrow
    alabel <- if (a == "neo") "Neonatal" else str_to_title(a)
    atitle <- if (a == "neo") {
        paste0("Physician Agreed\n", alabel, " Records\n(n=", arows, ", 100%)")
    } else {
        paste0("Physician Agreed ", alabel, " Records (n=", arows, ", 100%)")
    }
    aref <- str_to_title(a)
    
    # Get unique age range columns
    arange_col <- out %>% select(starts_with(sprintf(
            "PCCC %s Age Agree ",
            aref
        ))) %>%
        names
    
    # Create age range with counts
    arange_remap <- list()
    arange_counts <- list()
    for (ar_col in arange_col) {
        
        # Get cod without prefix
        ar <- gsub(sprintf("PCCC %s Age Agree ", aref), "", ar_col)
        
        # Calc num and perc cases for age range
        narange <- df %>% filter(age_range == ar & is_agreed == TRUE & age == a) %>% nrow
        parange <- (narange / arows) * 100
        parange_label <- if (round(parange) >= 1) round(parange) else "<1"
        
        # Add label for age ranges
        arange_remap[[ar]] <- paste0(
            ar,
            "\n(n=", narange, ", ", parange_label, "%)"
        )
        
        # Store age range count data
        arange_counts[[ar]] <- narange
    }
    
    # Prepare boxplot data
    ardata <- out %>%
        select(
            Model,
            starts_with(sprintf("PCCC %s Age Agree", aref))
        ) %>%
        rename_at(
             vars(-Model),
             ~gsub(sprintf("PCCC %s Age Agree ", aref), "", .)
        ) %>%
        filter(!str_detect(Model, "&")) %>% # remove multi models
        pivot_longer( # transform to long format
            cols = -Model,
            names_to = "Age Range",
            values_to = "PCCC"
        ) %>%
        mutate( # rename cod with counts
            "Age Range" = recode(`Age Range`, !!!arange_remap)
        ) %>%
        group_by(`Age Range`) %>%
        mutate( # Make values less than 0 equal to 0
            PCCC = if_else(PCCC <= 0, 0, PCCC)
        ) %>%
        mutate( # Add min, mid, and max model names to label
            "PCCC Min" = if_else(
                PCCC <= min(PCCC) + 0.05,
                paste0(
                    Model,
                    if_else(round(PCCC, 2) != round(min(PCCC), 2), paste0(" (", round(PCCC, 2), ")"), "")
                ),
                NA
            ),
            "PCCC Max" = if_else(
                PCCC >= max(PCCC) - 0.05,
                paste0(
                    Model,
                    if_else(round(PCCC, 2) != round(max(PCCC), 2), paste0(" (", round(PCCC, 2), ")"), "")
                ),
                NA
            ),
            "PCCC Mid" = if_else(
                is.na(`PCCC Min`) & is.na(`PCCC Max`),
                paste0(Model, "\n(", round(PCCC, 2), ")"),
                NA
            ),
            "PCCC Min" = if_else( # Combine into one row if close PCCC
                `PCCC` == min(PCCC),
                if_else(
                    sum(!is.na(`PCCC Min`)) > 1,
                    paste0(na.omit(`PCCC Min`), collapse = "\n"),
                    paste0(
                        str_replace(na.omit(`PCCC Min`), "\\s*\\(.*\\)", ""),
                        collapse = "\n"
                    )
                ),
                NA
            ),
            "PCCC Max" = if_else(
                `PCCC` == max(PCCC),
                if_else(
                    sum(!is.na(`PCCC Max`)) > 1,
                    paste0(na.omit(`PCCC Max`), collapse = "\n"),
                    paste0(str_replace(na.omit(`PCCC Max`), "\\s*\\(.*\\)", ""),
                           collapse = "\n")
                ),
                NA
            )
        )
    
    # Create cod order based on max pccc
    arorder <- ardata %>%
        group_by(`Age Range`) %>%
        summarise("PCCC Max Value" = max(PCCC, na.rm = TRUE)) %>%
        select(`Age Range`, `PCCC Max Value`) %>%
        arrange(desc(`PCCC Max Value`)) %>%
        pull(`Age Range`)
    ardata$`Age Range` <- factor(ardata$`Age Range`, levels = rev(arorder))
    
    # Plot boxplot
    arplot <- ardata %>%
        ggplot(aes(x = `Age Range`, y = PCCC)) +
        geom_boxplot(
            linewidth = 0.5,
            width = 0.25
        ) +
        geom_text(
            aes(label = `PCCC Min`),
            color = "#4d4d4d",
            size = 2.5,
            hjust = 1,
            position = position_nudge(y = -0.08)
        ) +
        geom_text(
            aes(label = `PCCC Max`),
            color = "#4d4d4d",
            size = 2.5,
            hjust = 0,
            position = position_nudge(y = 0.08)
        ) +
        stat_summary( # min pccc txt on boxplot
            geom = "text",
            fun = min,
            aes(label = sprintf("%1.2f", after_stat(y))),
            position = position_nudge(y = -0.04),
            size = 3
        ) +
        stat_summary( # max pccc txt on boxplot
            geom = "text",
            fun = max,
            aes(label = sprintf("%1.2f", after_stat(y))),
            position = position_nudge(y = 0.04),
            size = 3
        ) +
        labs(
            x = atitle,
            y = "PCCC (0=Low, 1=High)"
        ) +
        ylim(0.1, 1) +
        coord_flip() +
        theme_minimal() +
        theme(
            panel.grid = element_blank(),
            axis.line = element_blank(),
            axis.line.y = element_blank(),
            axis.title.x = element_text(margin = margin(t = 10)),
            axis.title.y = element_text(margin = margin(r = 10)),
            axis.line.x = element_line(
                color = "black",
                arrow = grid::arrow(length = unit(0.2, "cm"), ends = "both")
            )
        )
    
    # Save the plot
    print(arplot)
    ggsave(
        sprintf("../manuscript/fig-perf-agerange-%s.pdf", a),
        plot = arplot,
        dpi = 300,
        width = if (a == "adult") 6 else if (a == "neo") 6 else NA,
        height = if (a == "adult") 8 else if (a == "neo") 2.5 else NA
    )
}

COD Performance

Plot model performance for physician agreed records by cause of death category.

for (a in c("adult", "child", "neo")) {
    
    # Create label for age group
    arows <- df %>% filter(is_agreed == TRUE & age == a) %>% nrow
    alabel <- if (a == "neo") "Neonatal" else str_to_title(a)
    atitle <- paste0("Physician Agreed ", alabel, " Records (n=", arows, ", 100%)")
    aref <- str_to_title(a)
    
    # Get unique cause columns
    causes_col <- out %>% select(starts_with(sprintf(
        "PCCC %s COD Agree ", aref
    ))) %>% names
    
    # Create cod with counts
    cod_remap <- list()
    cod_counts <- list()
    for (cod_col in causes_col) {
        
        # Get cod without prefix
        cod <- gsub(sprintf("PCCC %s COD Agree ", aref), "", cod_col)
        
        # Calculate num and perc cases for cod
        ncod <- df %>% filter(physician_cghr10 == cod & is_agreed == TRUE & age == a) %>% nrow
        pcod <- (ncod / nagree) * 100
        pcod_label <- if (round(pcod) >= 1) round(pcod) else "<1"
        
        # Break cod into newlines if more than 3 words
        if (str_count(cod, "\\s+") > 3) {
            cod_label <- str_replace(cod, "(\\S+\\s+\\S+\\s+\\S+) ", "\\1\n")
        } else {
            cod_label <- cod
        }
        
        # Add label for cod
        cod_remap[[cod]] <- paste0(cod_label, "\n(n=", ncod, ", ", pcod_label, "%)")
        
        # Store cod count data
        cod_counts[[cod]] <- ncod
    }
    
    # Prepare boxplot data
    coddata <- out %>%
        select(
            Model,
            starts_with(sprintf("PCCC %s COD Agree ", aref))
        ) %>%
        rename_at(
             vars(-Model),
             ~gsub(sprintf("PCCC %s COD Agree ", aref), "", .)
        ) %>%
        filter(!str_detect(Model, "&")) %>% # remove multi models
        pivot_longer( # transform to long format
            cols = -Model,
            names_to = "COD",
            values_to = "PCCC"
        ) %>%
        mutate( # rename cod with counts
            "COD" = recode(`COD`, !!!cod_remap)
        ) %>%
        group_by(`COD`) %>%
        mutate( # Make values less than 0 equal to 0
            PCCC = if_else(PCCC <= 0, 0, PCCC)
        ) %>%
        mutate( # Add min, mid, and max model names to label
            "PCCC Min" = if_else(
                PCCC <= min(PCCC) + 0.05,
                paste0(
                    Model,
                    if_else(round(PCCC, 2) != round(min(PCCC), 2), paste0(" (", round(PCCC, 2), ")"), "")
                ),
                NA
            ),
            "PCCC Max" = if_else(
                PCCC >= max(PCCC) - 0.05,
                paste0(
                    Model,
                    if_else(round(PCCC, 2) != round(max(PCCC), 2), paste0(" (", round(PCCC, 2), ")"), "")
                ),
                NA
            ),
            "PCCC Mid" = if_else(
                is.na(`PCCC Min`) & is.na(`PCCC Max`),
                paste0(Model, "\n(", round(PCCC, 2), ")"),
                NA
            ),
            "PCCC Min" = if_else( # Combine into one row if close PCCC
                `PCCC` == min(PCCC),
                if_else(
                    sum(!is.na(`PCCC Min`)) > 1,
                    paste0(na.omit(`PCCC Min`), collapse = "\n"),
                    paste0(
                        str_replace(na.omit(`PCCC Min`), "\\s*\\(.*\\)", ""),
                        collapse = "\n"
                    )
                ),
                NA
            ),
            "PCCC Max" = if_else(
                `PCCC` == max(PCCC),
                if_else(
                    sum(!is.na(`PCCC Max`)) > 1,
                    paste0(na.omit(`PCCC Max`), collapse = "\n"),
                    paste0(str_replace(na.omit(`PCCC Max`), "\\s*\\(.*\\)", ""),
                           collapse = "\n")
                ),
                NA
            )
        )
    
    # Create cod order based on max pccc
    codorder <- coddata %>%
        group_by(COD) %>%
        summarise("PCCC Max Value" = max(PCCC, na.rm = TRUE)) %>%
        select(COD, `PCCC Max Value`) %>%
        arrange(desc(`PCCC Max Value`)) %>%
        pull(COD)
    coddata$COD <- factor(coddata$COD, levels = rev(codorder))
    
    # Plot boxplot
    codplot <- coddata %>%
        ggplot(aes(x = COD, y = PCCC)) +
        geom_boxplot(
            linewidth = 0.5,
            width = 0.25
        ) +
        geom_text(
            aes(label = `PCCC Min`),
            color = "#4d4d4d",
            size = 2.5,
            hjust = 1,
            position = position_nudge(y = -0.12)
        ) +
        geom_text(
            aes(label = `PCCC Max`),
            color = "#4d4d4d",
            size = 2.5,
            hjust = 0,
            position = position_nudge(y = 0.12)
        ) +
        stat_summary( # min pccc txt on boxplot
            geom = "text",
            fun = min,
            aes(label = sprintf("%1.2f", after_stat(y))),
            position = position_nudge(y = -0.06),
            size = 3
        ) +
        stat_summary( # max pccc txt on boxplot
            geom = "text",
            fun = max,
            aes(label = sprintf("%1.2f", after_stat(y))),
            position = position_nudge(y = 0.06),
            size = 3
        ) +
        labs(
            x = atitle,
            y = "PCCC (0=Low, 1=High)"
        ) +
        ylim(-0.3, 1.3) +
        coord_flip() +
        theme_minimal() +
        theme(
            panel.grid = element_blank(),
            axis.line = element_blank(),
            axis.line.y = element_blank(),
            axis.title.x = element_text(margin = margin(t = 10)),
            axis.title.y = element_text(margin = margin(r = 10)),
            axis.line.x = element_line(
                color = "black",
                arrow = grid::arrow(length = unit(0.2, "cm"), ends = "both")
            )
        )
    
    # Save the plot
    print(codplot)
    ggsave(
        sprintf("../manuscript/fig-perf-cod-%s.pdf", a),
        plot = codplot,
        dpi = 300,
        width = if (a == "adult") 8 else NA,
        height = if (a == "adult") 10 else NA
    )
}

LS0tCnRpdGxlOiAiTWV0cmljcyIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgphdXRob3I6ICJSaWNoYXJkIFdlbiA8cmljaGFyZC53ZW5AdW5pdHloZWFsdGgudG8+IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIG1lc3NhZ2U9RkFMU0UsCiAgd2FybmluZz1GQUxTRQopCm9wdGlvbnMocmVhZHIuc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKYGBgCgojIyBMaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KY2F0KCJSIFZlcnNpb25cbi0tLSIpClIudmVyc2lvbgoKY2F0KCJcblBhY2thZ2UgVmVyc2lvbnNcbi0tLSIpCmNhdChwYXN0ZTAoIlxudGlkeXZlcnNlICIsIHBhY2thZ2VWZXJzaW9uKCJ0aWR5dmVyc2UiKSkpCmBgYAoKIyMgU2V0dGluZ3MKCmBgYHtyfQptb2RlbHNfZm9ybWF0IDwtIGMoCiAgICAiX2NnaHIxMCIgPSAiIiwKICAgICJfIiA9ICIgJiAiLAogICAgImNoYXRncHQzIiA9ICJDaGF0R1BULTMuNSIsCiAgICAiY2hhdGdwdDQiID0gIkNoYXRHUFQtNCIsCiAgICAiaW5zaWxpY292YSIgPSAiSW5TaWxpY29WQSIsCiAgICAiaW50ZXJ2YTUiID0gIkludGVyVkEtNSIKKQpzdGFnZV9mb3JtYXQgPC0gYygKICAgICJpc18iID0gIiIsCiAgICAicmVjb24iID0gIlJlY29uY2lsaWF0aW9uIiwKICAgICJhZ3JlZWQiID0gIkFncmVlbWVudCIsCiAgICAiYWRqIiA9ICJBZGp1ZGljYXRpb24iCikKYGBgCgojIyBGdW5jdGlvbnMKCkZ1bmN0aW9ucyBmb3IgUENDQyBhbmQgQ1NNRiBBY2N1cmFjeS4KCmBgYHtyfQoKIyBDYWxjIFBDQ0MKY2FsY19wY2NjIDwtIGZ1bmN0aW9uKGFjdHVhbCwgcHJlZCwgayA9IDEsIE4gPSBOVUxMKSB7CiAgICAKICAgICMgQ2FsYyBOIG51bSBvZiBjYXVzZXMgaWYgbm90IGtub3duCiAgICBOIDwtIGlmIChpcy5udWxsKE4pKSBsZW5ndGgodW5pcXVlKG5hLm9taXQoYyhhY3R1YWwsIHByZWQpKSkpIGVsc2UgTgogICAgCiAgICAjIENhbGMgZnJhYyBvZiBkZWF0aHMgaW4gdG9wIGsgY2F1c2VzCiAgICBUUCA8LSBhY3R1YWwgPT0gcHJlZAogICAgVFBbaXMubmEoVFApIHwgaXMubnVsbChUUCldIDwtIEZBTFNFICMgZm9yIG5vIHByZWRzCiAgICBDIDwtIHN1bShUUCkgLyBsZW5ndGgoYWN0dWFsKQogICAgCiAgICAjIENhbGMgUENDQwogICAgb3V0IDwtIChDIC0gKGsvTikpIC8gKDEgLSAoay9OKSkKICAgIHJldHVybihvdXQpCn0KCiMgQ2FsYyBDU01GIGFjY3VyYWN5CmNhbGNfY3NtZl9hY2MgPC0gZnVuY3Rpb24oYWN0dWFsLCBwcmVkKSB7CiAgICAKICAgICMgR2V0IGFsbCB1bmlxdWUgY2F1c2VzCiAgICBjYXVzZXMgPC0gdW5pcXVlKGMoYWN0dWFsLCBwcmVkKSkKICAgIAogICAgIyBHZXQgY3NtZnMKICAgIGNhc2VzIDwtIGxlbmd0aChhY3R1YWwpCiAgICBjc21mX3RydWUgPC0gdGFibGUoYWN0dWFsKSAvIGNhc2VzCiAgICBjc21mX3ByZWQgPC0gdGFibGUocHJlZCkgLyBjYXNlcwogICAgCiAgICAjIENvcnJlY3QgZm9yIG1pc3NpbmcgY2F1c2VzIGluIGVpdGhlciBhY3R1YWwgb3IgcHJlZAogICAgY3NtZl90cnVlIDwtIHZhcHBseSgKICAgICAgICBjYXVzZXMsCiAgICAgICAgZnVuY3Rpb24oeCkgaWYgKHggJWluJSBuYW1lcyhjc21mX3RydWUpKSBjc21mX3RydWVbeF0gZWxzZSAwLAogICAgICAgIEZVTi5WQUxVRSA9IG51bWVyaWMoMSkKICAgICkKICAgIGNzbWZfcHJlZCA8LSB2YXBwbHkoCiAgICAgICAgY2F1c2VzLAogICAgICAgIGZ1bmN0aW9uKHgpIGlmICh4ICVpbiUgbmFtZXMoY3NtZl9wcmVkKSkgY3NtZl9wcmVkW3hdIGVsc2UgMCwKICAgICAgICBGVU4uVkFMVUUgPSBudW1lcmljKDEpCiAgICApCiAgICAKICAgICMgQ2FsYyBjc21mIG1heCBlcnJvcgogICAgY3NtZl9tYXhfZXJyb3IgPC0gMiAqICgxIC0gbWluKGNzbWZfdHJ1ZSkpCiAgICAKICAgICMgQ2FsYyBjc21mIGFjYwogICAgb3V0IDwtIDEgLSAoc3VtKGFicyhjc21mX3RydWUgLSBjc21mX3ByZWQpKSAvIGNzbWZfbWF4X2Vycm9yKQogICAgcmV0dXJuKG91dCkKfQoKIyBCdWxrIGNhbGMgcGVyIG1vZGVsCmNhbGNfcGVyX21vZGVsIDwtIGZ1bmN0aW9uKGFjdHVhbCwgcHJlZCwgZnVuYywgbmFtZSA9ICJNZXRyaWMiLCAuLi4pIHsKICAgIAogICAgIyBQcmVwIGRhdGEgaW4gbG9uZyBmb3JtYXQgZ3JvdXBlZCBieSBtb2RlbAogICAgb3V0IDwtIHByZWQgJT4lCiAgICAgICAgcGl2b3RfbG9uZ2VyKCAjIHRvIGxvbmcgZm9ybWF0CiAgICAgICAgICAgIGV2ZXJ5dGhpbmcoKSwKICAgICAgICAgICAgbmFtZXNfdG8gPSAiTW9kZWwiLAogICAgICAgICAgICB2YWx1ZXNfdG8gPSAicHJlZGljdGlvbiIKICAgICAgICApICU+JSBncm91cF9ieShNb2RlbCkKICAgIAogICAgIyBDYWxjIGsgaWYgcGNjYyBhcyBpdCBpcyBkaWZmIGZvciBtb2RlbCBjb21ib3MKICAgIGlmIChpZGVudGljYWwoZnVuYywgY2FsY19wY2NjKSkgewogICAgICAgIG91dCA8LSBvdXQgJT4lCiAgICAgICAgICAgIHN1bW1hcmlzZSgKICAgICAgICAgICAgICAgICEhbmFtZSA6PSBmdW5jKAogICAgICAgICAgICAgICAgICAgIGFjdHVhbCwKICAgICAgICAgICAgICAgICAgICBwcmVkaWN0aW9uLAogICAgICAgICAgICAgICAgICAgIGsgPSBzdHJfY291bnQodW5pcXVlKE1vZGVsKSwgIl8iKSwKICAgICAgICAgICAgICAgICAgICAuLi4KICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgfSBlbHNlIHsgIyBvdGhlcndpc2UgYXBwbHkgZnVuYwogICAgICAgIG91dCA8LSBvdXQgJT4lCiAgICAgICAgICAgIHN1bW1hcmlzZSgKICAgICAgICAgICAgICAgICEhbmFtZSA6PSBmdW5jKAogICAgICAgICAgICAgICAgICAgIGFjdHVhbCwKICAgICAgICAgICAgICAgICAgICBwcmVkaWN0aW9uLAogICAgICAgICAgICAgICAgICAgIC4uLgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICApCiAgICB9CiAgICAKICAgICMgRm9ybWF0IGRpc3BsYXkgYW5kIHJldHVybgogICAgb3V0IDwtIG91dCAlPiUKICAgICAgICBtdXRhdGUoTW9kZWwgPSBzdHJfcmVwbGFjZV9hbGwoTW9kZWwsIG1vZGVsc19mb3JtYXQpKSAlPiUKICAgICAgICBhcnJhbmdlKGFjcm9zcyh7e25hbWV9fSwgZGVzYykpCiAgICByZXR1cm4ob3V0KQp9CgojIEJ1bGsgY2FsYyBwZXIgbW9kZWwgYmFzZWQgb24gYWdlLCBwaHlzaWNpYW4gY29kaW5nIHN0YWdlLCBhbmQgY2F1c2Ugb2YgZGVhdGgKY2FsY19ieSA8LSBmdW5jdGlvbigKICAgICAgICBkZiwKICAgICAgICBmdW5jLAogICAgICAgIG5hbWUgPSAiTWV0cmljIiwKICAgICAgICBieV9hZ2UgPSBUUlVFLAogICAgICAgIGJ5X3N0YWdlID0gVFJVRSwKICAgICAgICBieV9zZXggPSBUUlVFLAogICAgICAgIGJ5X2NvZCA9IFRSVUUsCiAgICAgICAgYnlfYWdlX3JhbmdlID0gVFJVRSwKICAgICAgICAuLi4KICAgICkgewogICAgb3V0IDwtIGxpc3QoKQogICAgCiAgICAjIENhbGMgb3ZlcmFsbCBtZXRyaWMKICAgIG91dFtbbmFtZV1dIDwtIGNhbGNfcGVyX21vZGVsKAogICAgICAgIGFjdHVhbCA9IGRmICU+JSBwdWxsKHBoeXNpY2lhbl9jZ2hyMTApLAogICAgICAgIHByZWQgPSBkZiAlPiUgc2VsZWN0KGVuZHNfd2l0aCgiX2NnaHIxMCIpLCAtcGh5c2ljaWFuX2NnaHIxMCksCiAgICAgICAgZnVuYyA9IGZ1bmMsCiAgICAgICAgLi4uCiAgICApICU+JSByZW5hbWUoCiAgICAgICAge3sgbmFtZSB9fSA6PSBNZXRyaWMKICAgICkKICAgIAogICAgIyBDYWxjIG1ldHJpYyBmb3IgdmEgY29kaW5nIHN0YWdlCiAgICBpZiAoYnlfc3RhZ2UpIHsKICAgICAgICBmb3IgKHN0YWdlIGluIGMoImlzX2FncmVlZCIsICJpc19yZWNvbiIsICJpc19hZGoiKSkgewogICAgICAgICAgICAKICAgICAgICAgICAgIyBGaWx0ZXIgZGF0YSBmb3Igc3RhZ2UKICAgICAgICAgICAgZGZfZmlsdGVyIDwtIGRmICU+JSBmaWx0ZXIoLltbc3RhZ2VdXSA9PSBUUlVFKQogICAgICAgICAgICBtZXRyaWNfbmFtZSA9IHBhc3RlMCgKICAgICAgICAgICAgICAgIG5hbWUsCiAgICAgICAgICAgICAgICAiICIsCiAgICAgICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoc3RhZ2UsIHN0YWdlX2Zvcm1hdCkKICAgICAgICAgICAgKQogICAgICAgICAgICAKICAgICAgICAgICAgIyBDYWxjIG1ldHJpYyBmb3Igc3RhZ2UKICAgICAgICAgICAgb3V0W1ttZXRyaWNfbmFtZV1dIDwtIGNhbGNfcGVyX21vZGVsKAogICAgICAgICAgICAgICAgYWN0dWFsID0gZGZfZmlsdGVyICU+JQogICAgICAgICAgICAgICAgICAgIHB1bGwocGh5c2ljaWFuX2NnaHIxMCksCiAgICAgICAgICAgICAgICBwcmVkID0gZGZfZmlsdGVyICU+JQogICAgICAgICAgICAgICAgICAgIHNlbGVjdChlbmRzX3dpdGgoIl9jZ2hyMTAiKSwgLXBoeXNpY2lhbl9jZ2hyMTApLAogICAgICAgICAgICAgICAgZnVuYyA9IGZ1bmMsCiAgICAgICAgICAgICAgICAuLi4KICAgICAgICAgICAgKSAlPiUgcmVuYW1lKAogICAgICAgICAgICAgICAgIHt7IG1ldHJpY19uYW1lIH19IDo9IE1ldHJpYwogICAgICAgICAgICApCiAgICAgICAgfQogICAgfQogICAgCiAgICAjIENhbGMgcGNjIGZvciBlYWNoIGFnZSBncm91cAogICAgaWYgKGJ5X2FnZSkgewogICAgICAgIGZvciAoYWdlX2dyb3VwIGluIGMoImFkdWx0IiwgImNoaWxkIiwgIm5lbyIpKSB7CiAgICAgICAgICAgIAogICAgICAgICAgICAjIEZpbHRlciBkYXRhIGZvciBhZ2UKICAgICAgICAgICAgZGZfZmlsdGVyIDwtIGRmICU+JSBmaWx0ZXIoYWdlID09IGFnZV9ncm91cCkKICAgICAgICAgICAgbWV0cmljX25hbWUgPSBwYXN0ZTAoCiAgICAgICAgICAgICAgICBuYW1lLAogICAgICAgICAgICAgICAgIiAiLAogICAgICAgICAgICAgICAgc3RyX3RvX3RpdGxlKGFnZV9ncm91cCkKICAgICAgICAgICAgKQogICAgICAgIAogICAgICAgICAgICAjIENhbGMgbWV0cmljIGZvciBhZ2UKICAgICAgICAgICAgb3V0W1ttZXRyaWNfbmFtZV1dIDwtIGNhbGNfcGVyX21vZGVsKAogICAgICAgICAgICAgICAgYWN0dWFsID0gZGZfZmlsdGVyICU+JQogICAgICAgICAgICAgICAgICAgIHB1bGwocGh5c2ljaWFuX2NnaHIxMCksCiAgICAgICAgICAgICAgICBwcmVkID0gZGZfZmlsdGVyICU+JQogICAgICAgICAgICAgICAgICAgIHNlbGVjdChlbmRzX3dpdGgoIl9jZ2hyMTAiKSwgLXBoeXNpY2lhbl9jZ2hyMTApLAogICAgICAgICAgICAgICAgZnVuYyA9IGZ1bmMsCiAgICAgICAgICAgICAgICAuLi4KICAgICAgICAgICAgKSAlPiUgcmVuYW1lKAogICAgICAgICAgICAgICAgIHt7IG1ldHJpY19uYW1lIH19IDo9IE1ldHJpYwogICAgICAgICAgICApCiAgICAgICAgICAgIAogICAgICAgICAgICAjIENhbGMgbWV0cmljIGZvciBlYWNoIHN0YWdlIHdpdGhpbiBhZ2UgZ3JvdXAKICAgICAgICAgICAgZm9yIChzdGFnZSBpbiBjKCJpc19hZ3JlZWQiLCAiaXNfcmVjb24iLCAiaXNfYWRqIikpIHsKICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAjIEZpbHRlciBkYXRhIGZvciBzdGFnZSB3aXRoaW4gYWdlIGdyb3VwCiAgICAgICAgICAgICAgICBkZl9maWx0ZXIgPC0gZGYgJT4lIGZpbHRlcigKICAgICAgICAgICAgICAgICAgICAuW1tzdGFnZV1dID09IFRSVUUgJiBhZ2UgPT0gYWdlX2dyb3VwCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBtZXRyaWNfbmFtZSA8LSBwYXN0ZTAoCiAgICAgICAgICAgICAgICAgICAgbmFtZSwKICAgICAgICAgICAgICAgICAgICAiICIsCiAgICAgICAgICAgICAgICAgICAgc3RyX3RvX3RpdGxlKGFnZV9ncm91cCksCiAgICAgICAgICAgICAgICAgICAgIiAiLAogICAgICAgICAgICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbChzdGFnZSwgc3RhZ2VfZm9ybWF0KQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAjIENhbGMgbWV0cmljIGZvciBzdGFnZSB3aXRoaW4gYWdlIGdyb3VwCiAgICAgICAgICAgICAgICBvdXRbW21ldHJpY19uYW1lXV0gPC0gY2FsY19wZXJfbW9kZWwoCiAgICAgICAgICAgICAgICAgICAgYWN0dWFsID0gZGZfZmlsdGVyICU+JQogICAgICAgICAgICAgICAgICAgICAgICBwdWxsKHBoeXNpY2lhbl9jZ2hyMTApLAogICAgICAgICAgICAgICAgICAgIHByZWQgPSBkZl9maWx0ZXIgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChlbmRzX3dpdGgoIl9jZ2hyMTAiKSwgLXBoeXNpY2lhbl9jZ2hyMTApLAogICAgICAgICAgICAgICAgICAgIGZ1bmMgPSBmdW5jLAogICAgICAgICAgICAgICAgICAgIC4uLgogICAgICAgICAgICAgICAgKSAlPiUgcmVuYW1lKAogICAgICAgICAgICAgICAgICAgICB7eyBtZXRyaWNfbmFtZSB9fSA6PSBNZXRyaWMKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KICAgIAogICAgIyBDYWxjIG1ldHJpYyBieSBzZXggZm9yIGVhY2ggYWdlIGdyb3VwCiAgICBpZiAoYnlfc2V4KSB7CiAgICAgICAgCiAgICAgICAgIyBDYWxjIG1ldHJpYyBieSBjb2QKICAgICAgICBmb3IgKGEgaW4gYygiYWR1bHQiLCAiY2hpbGQiLCAibmVvIikpIHsKICAgICAgICAgICAgZm9yIChzeCBpbiBjKCJNYWxlIiwgIkZlbWFsZSIpKSB7CiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICMgRmlsdGVyIGRhdGEgZm9yIGNvZAogICAgICAgICAgICAgICAgZGZfZmlsdGVyIDwtIGRmICU+JSBmaWx0ZXIoCiAgICAgICAgICAgICAgICAgICAgc2V4ID09IHN4ICYgaXNfYWdyZWVkID09IFRSVUUgJiBhZ2UgPT0gYQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgbWV0cmljX25hbWUgPC0gc3ByaW50ZigKICAgICAgICAgICAgICAgICAgICAiJXMgJXMgU2V4IEFncmVlICVzIiwgbmFtZSwgc3RyX3RvX3RpdGxlKGEpLCBzeAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAjIENhbGMgbWV0cmljIGZvciBjb2QKICAgICAgICAgICAgICAgIGlmIChucm93KGRmX2ZpbHRlcikgPiAwKSB7CiAgICAgICAgICAgICAgICAgICAgb3V0W1ttZXRyaWNfbmFtZV1dIDwtIGNhbGNfcGVyX21vZGVsKAogICAgICAgICAgICAgICAgICAgICAgICBhY3R1YWwgPSBkZl9maWx0ZXIgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKHBoeXNpY2lhbl9jZ2hyMTApLAogICAgICAgICAgICAgICAgICAgICAgICBwcmVkID0gZGZfZmlsdGVyICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGVuZHNfd2l0aCgiX2NnaHIxMCIpLCAtcGh5c2ljaWFuX2NnaHIxMCksCiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmMgPSBmdW5jLAogICAgICAgICAgICAgICAgICAgICAgICAuLi4KICAgICAgICAgICAgICAgICAgICApICU+JSByZW5hbWUoCiAgICAgICAgICAgICAgICAgICAgICAgICB7eyBtZXRyaWNfbmFtZSB9fSA6PSBNZXRyaWMKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CiAgICAKICAgICMgQ2FsYyBtZXRyaWMgYnkgY2F1c2Ugb2YgZGVhdGggZm9yIGVhY2ggYWdlIGdyb3VwCiAgICBpZiAoYnlfY29kKSB7CiAgICAgICAgCiAgICAgICAgIyBHZXQgdW5pcXVlIGNhdXNlcyBvZiBkZWF0aAogICAgICAgIGNhdXNlcyA8LSBkZiAlPiUKICAgICAgICAgICAgc2VsZWN0KGVuZHNfd2l0aCgiX2NnaHIxMCIpKSAlPiUKICAgICAgICAgICAgcGl2b3RfbG9uZ2VyKAogICAgICAgICAgICAgICAgZXZlcnl0aGluZygpLAogICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiY29sdW1uIiwKICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJjb2QiCiAgICAgICAgICAgICkgJT4lCiAgICAgICAgICAgIGRpc3RpbmN0KGNvZCkgJT4lCiAgICAgICAgICAgIGZpbHRlcighaXMubmEoY29kKSkgJT4lCiAgICAgICAgICAgIHB1bGwoY29kKQogICAgICAgIAogICAgICAgICMgQ2FsYyBtZXRyaWMgYnkgY29kCiAgICAgICAgZm9yIChhIGluIGMoImFkdWx0IiwgImNoaWxkIiwgIm5lbyIpKSB7CiAgICAgICAgICAgIGZvciAoY29kIGluIGNhdXNlcykgewogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAjIEZpbHRlciBkYXRhIGZvciBjb2QKICAgICAgICAgICAgICAgIGRmX2ZpbHRlciA8LSBkZiAlPiUgZmlsdGVyKAogICAgICAgICAgICAgICAgICAgIHBoeXNpY2lhbl9jZ2hyMTAgPT0gY29kICYgaXNfYWdyZWVkID09IFRSVUUgJiBhZ2UgPT0gYQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgbWV0cmljX25hbWUgPC0gc3ByaW50ZigKICAgICAgICAgICAgICAgICAgICAiJXMgJXMgQ09EIEFncmVlICVzIiwgbmFtZSwgc3RyX3RvX3RpdGxlKGEpLCBjb2QKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgIyBDYWxjIG1ldHJpYyBmb3IgY29kCiAgICAgICAgICAgICAgICBpZiAobnJvdyhkZl9maWx0ZXIpID4gMCkgewogICAgICAgICAgICAgICAgICAgIG91dFtbbWV0cmljX25hbWVdXSA8LSBjYWxjX3Blcl9tb2RlbCgKICAgICAgICAgICAgICAgICAgICAgICAgYWN0dWFsID0gZGZfZmlsdGVyICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsbChwaHlzaWNpYW5fY2docjEwKSwKICAgICAgICAgICAgICAgICAgICAgICAgcHJlZCA9IGRmX2ZpbHRlciAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChlbmRzX3dpdGgoIl9jZ2hyMTAiKSwgLXBoeXNpY2lhbl9jZ2hyMTApLAogICAgICAgICAgICAgICAgICAgICAgICBmdW5jID0gZnVuYywKICAgICAgICAgICAgICAgICAgICAgICAgLi4uCiAgICAgICAgICAgICAgICAgICAgKSAlPiUgcmVuYW1lKAogICAgICAgICAgICAgICAgICAgICAgICAge3sgbWV0cmljX25hbWUgfX0gOj0gTWV0cmljCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQogICAgCiAgICAjIENhbGMgbWV0cmljIGJ5IGFnZSByYW5nZQogICAgaWYgKGJ5X2FnZV9yYW5nZSkgewogICAgICAgIAogICAgICAgICMgR2V0IHVuaXF1ZSBhZ2UgcmFuZ2VzCiAgICAgICAgYWdlX3JhbmdlcyA8LSBkZiAlPiUKICAgICAgICAgICAgcHVsbChhZ2VfcmFuZ2UpICU+JQogICAgICAgICAgICB1bmlxdWUKICAgICAgICAKICAgICAgICAjIENhbGMgbWV0cmljIGJ5IGFnZSByYW5nZQogICAgICAgIGZvciAoYSBpbiBjKCJhZHVsdCIsICJjaGlsZCIsICJuZW8iKSkgewogICAgICAgICAgICBmb3IgKGFyIGluIGFnZV9yYW5nZXMpIHsKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgIyBGaWx0ZXIgZm9yIGFnZSByYW5nZQogICAgICAgICAgICAgICAgZGZfZmlsdGVyIDwtIGRmICU+JSBmaWx0ZXIoCiAgICAgICAgICAgICAgICAgICAgYWdlX3JhbmdlID09IGFyICYgaXNfYWdyZWVkID09IFRSVUUgJiBhZ2UgPT0gYQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgbWV0cmljX25hbWUgPC0gc3ByaW50ZigKICAgICAgICAgICAgICAgICAgICAiJXMgJXMgQWdlIEFncmVlICVzIiwgbmFtZSwgc3RyX3RvX3RpdGxlKGEpLCBhcgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAjIENhbGMgbWV0cmljIGZvciBjb2QKICAgICAgICAgICAgICAgIGlmIChucm93KGRmX2ZpbHRlcikgPiAwKSB7CiAgICAgICAgICAgICAgICAgICAgb3V0W1ttZXRyaWNfbmFtZV1dIDwtIGNhbGNfcGVyX21vZGVsKAogICAgICAgICAgICAgICAgICAgICAgICBhY3R1YWwgPSBkZl9maWx0ZXIgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKHBoeXNpY2lhbl9jZ2hyMTApLAogICAgICAgICAgICAgICAgICAgICAgICBwcmVkID0gZGZfZmlsdGVyICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGVuZHNfd2l0aCgiX2NnaHIxMCIpLCAtcGh5c2ljaWFuX2NnaHIxMCksCiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmMgPSBmdW5jLAogICAgICAgICAgICAgICAgICAgICAgICAuLi4KICAgICAgICAgICAgICAgICAgICApICU+JSByZW5hbWUoCiAgICAgICAgICAgICAgICAgICAgICAgICB7eyBtZXRyaWNfbmFtZSB9fSA6PSBNZXRyaWMKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CiAgICAKICAgICMgQ29tYmluZSBhbGwgcGNjYyBtZXRyaWNzCiAgICBvdXQgPC0gcmVkdWNlKG91dCwKICAgICAgICBmdW5jdGlvbih4LCB5KSBsZWZ0X2pvaW4oeCwgeSwgYnkgPSAiTW9kZWwiKQogICAgKSAlPiUKICAgICAgICBhcnJhbmdlKGFjcm9zcyh7eyBuYW1lIH19LCBkZXNjKSkKICAgIHJldHVybihvdXQpCn0KYGBgCgojIyBEYXRhCgojIyMgUmF3IERhdGEKCkxvYWQgZGF0YSBmcm9tIGBkYXRhYCBmb2xkZXIuCgpgYGB7cn0KcmF3X2RmIDwtIHJlYWRfY3N2KCIuLi9kYXRhL2hlYWxzbF9yZDF0bzJfY29kX3YxLmNzdiIpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmNhdCgiQ2FzZXM6ICIsIG5yb3cocmF3X2RmKSwgIlxuIikKcHJpbnQoaGVhZChyYXdfZGYpKQpgYGAKCiMjIyBDbGVhbiBMYWJlbHMKCkNsZWFuIGBhZ2VfcmFuZ2VgIGxhYmVscyB0byBpbmNsdWRlIG9ubHkgdmFsdWVzIGFuZCB1bml0IG9mIG1lYXN1cmUgaW4gdGl0bGVjYXNlLgoKYGBge3J9CmRmIDwtIHJhd19kZiAlPiUgbXV0YXRlKAogICAgImFnZV9yYW5nZSIgPSBzdHJfdG9fdGl0bGUoc3RyX3JlcGxhY2UoCiAgICAgICAgYWdlX3JhbmdlLAogICAgICAgICJcXHMqXFwoLipcXCkiLAogICAgICAgICIiCiAgICApKSwKICAgICJhZ2VfcmFuZ2UiID0gaWZfZWxzZSgKICAgICAgICAhc3RyX2RldGVjdChhZ2VfcmFuZ2UsICJZZWFyfFllYXJzfERheXxEYXlzfG1vbnRofE1vbnRoc3x3ZWVrfFdlZWtzIiksCiAgICAgICAgcGFzdGUwKGFnZV9yYW5nZSwgIiBZZWFycyIpLAogICAgICAgIGFnZV9yYW5nZQogICAgKQopCmRmCmBgYAoKIyMjIFBoeXNpY2lhbiBDYXNlcwoKRmlsdGVyIGZvciBjYXNlcyB0aGF0IHdlcmUgY29kZWQgYnkgcGh5c2ljaWFucy4KCmBgYHtyfQpkZiA8LSBkZiAlPiUKICAgIGZpbHRlcighaXMubmEocGh5c2ljaWFuX2NnaHIxMCkgJiAhaXMubnVsbChwaHlzaWNpYW5fY2docjEwKSkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KY2F0KCJQaHlzaWNpYW4gQ29kZWQgQ2FzZXM6ICIsIG5yb3coZGYpKQpwcmludChoZWFkKGRmKSkKYGBgCgojIyMgQ29tYmluZWQgTW9kZWxzCgpDcmVhdGUgbW9kZWwgY29tYmluYXRpb25zIHdoZXJlIGlmIGFueSBvZiB0aGUgbW9kZWxzIGhhdmUgdGhlIHBoeXNpY2lhbiBjb2RlLCB0aGVuIHNldCB0aGUgY29tYmluZWQgbW9kZWwncyBvdXRwdXQgZm9yIHRoZSBjYXNlIHRvIGJlIHRoZSBwaHlzaWNpYW4gY29kZS4gCgpgYGB7cn0KZGYgPC0gZGYgJT4lCiAgICBtdXRhdGUoICMgQ29tYmluZSBtb2RlbHMKICAgICAgICAiY2hhdGdwdDNfaW5zaWxpY292YV9jZ2hyMTAiID0gaWZfZWxzZSgKICAgICAgICAgICAgY2hhdGdwdDNfY2docjEwID09IHBoeXNpY2lhbl9jZ2hyMTAgfCAKICAgICAgICAgICAgICAgIGluc2lsaWNvdmFfY2docjEwID09IHBoeXNpY2lhbl9jZ2hyMTAsCiAgICAgICAgICAgIHBoeXNpY2lhbl9jZ2hyMTAsCiAgICAgICAgICAgIGNoYXRncHQzX2NnaHIxMAogICAgICAgICksCiAgICAgICAgImNoYXRncHQ0X2luc2lsaWNvdmFfY2docjEwIiA9IGlmX2Vsc2UoCiAgICAgICAgICAgIGNoYXRncHQ0X2NnaHIxMCA9PSBwaHlzaWNpYW5fY2docjEwIHwgCiAgICAgICAgICAgICAgICBpbnNpbGljb3ZhX2NnaHIxMCA9PSBwaHlzaWNpYW5fY2docjEwLAogICAgICAgICAgICBwaHlzaWNpYW5fY2docjEwLAogICAgICAgICAgICBjaGF0Z3B0NF9jZ2hyMTAKICAgICAgICApLAogICAgICAgICJjaGF0Z3B0M19pbnRlcnZhNV9jZ2hyMTAiID0gaWZfZWxzZSgKICAgICAgICAgICAgY2hhdGdwdDNfY2docjEwID09IHBoeXNpY2lhbl9jZ2hyMTAgfCAKICAgICAgICAgICAgICAgIGludGVydmE1X2NnaHIxMCA9PSBwaHlzaWNpYW5fY2docjEwLAogICAgICAgICAgICBwaHlzaWNpYW5fY2docjEwLAogICAgICAgICAgICBjaGF0Z3B0M19jZ2hyMTAKICAgICAgICApLAogICAgICAgICJjaGF0Z3B0NF9pbnRlcnZhNV9jZ2hyMTAiID0gaWZfZWxzZSgKICAgICAgICAgICAgY2hhdGdwdDRfY2docjEwID09IHBoeXNpY2lhbl9jZ2hyMTAgfCAKICAgICAgICAgICAgICAgIGludGVydmE1X2NnaHIxMCA9PSBwaHlzaWNpYW5fY2docjEwLAogICAgICAgICAgICBwaHlzaWNpYW5fY2docjEwLAogICAgICAgICAgICBjaGF0Z3B0NF9jZ2hyMTAKICAgICAgICApLAogICAgICAgICJjaGF0Z3B0M19pbnNpbGljb3ZhX2ludGVydmE1X2NnaHIxMCIgPSBpZl9lbHNlKAogICAgICAgICAgICBjaGF0Z3B0M19jZ2hyMTAgPT0gcGh5c2ljaWFuX2NnaHIxMCB8IAogICAgICAgICAgICAgICAgaW5zaWxpY292YV9jZ2hyMTAgPT0gcGh5c2ljaWFuX2NnaHIxMCB8CiAgICAgICAgICAgICAgICBpbnRlcnZhNV9jZ2hyMTAgPT0gcGh5c2ljaWFuX2NnaHIxMCwKICAgICAgICAgICAgcGh5c2ljaWFuX2NnaHIxMCwKICAgICAgICAgICAgY2hhdGdwdDNfY2docjEwCiAgICAgICAgKSwKICAgICAgICAiY2hhdGdwdDRfaW5zaWxpY292YV9pbnRlcnZhNV9jZ2hyMTAiID0gaWZfZWxzZSgKICAgICAgICAgICAgY2hhdGdwdDRfY2docjEwID09IHBoeXNpY2lhbl9jZ2hyMTAgfCAKICAgICAgICAgICAgICAgIGluc2lsaWNvdmFfY2docjEwID09IHBoeXNpY2lhbl9jZ2hyMTAgfAogICAgICAgICAgICAgICAgaW50ZXJ2YTVfY2docjEwID09IHBoeXNpY2lhbl9jZ2hyMTAsCiAgICAgICAgICAgIHBoeXNpY2lhbl9jZ2hyMTAsCiAgICAgICAgICAgIGNoYXRncHQ0X2NnaHIxMAogICAgICAgICkKICAgICkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KcHJpbnQoaGVhZChkZiAlPiUgc2VsZWN0KAogICAgY2hhdGdwdDNfaW5zaWxpY292YV9jZ2hyMTAsCiAgICBjaGF0Z3B0NF9pbnNpbGljb3ZhX2NnaHIxMCwKICAgIGNoYXRncHQzX2ludGVydmE1X2NnaHIxMCwKICAgIGNoYXRncHQ0X2ludGVydmE1X2NnaHIxMCwKICAgIGNoYXRncHQzX2luc2lsaWNvdmFfaW50ZXJ2YTVfY2docjEwLAogICAgY2hhdGdwdDRfaW5zaWxpY292YV9pbnRlcnZhNV9jZ2hyMTAKKSkpCmBgYAoKIyMjIFByZXBhcmVkIERhdGEKCkRpc3BsYXkgY2FzZSBjb3VudHMgZm9yIHJhdyBkYXRhIGFuZCBwcmVwYXJlZCBkYXRhIGFmdGVyIGNvbWJpbmluZyBtb2RlbHMgYW5kIGZpbHRlcmluZyBmb3IgcGh5c2ljaWFuIGNvZGVkIGNhc2VzLgoKYGBge3J9CgpjYXQoIlxuUmF3IERhdGFcbi0tLS0tLS0tXG5cbiIpCgojIFBoeXNpY2lhbiBpbmZvCnJhd19waHlzaWNpYW5zIDwtIHJhd19kZiRwaHlzaWNpYW5fY2docjEwCmNhdChwYXN0ZTAoCiAgICAiQ2FzZXMgKCIsCiAgICBsZW5ndGgodW5pcXVlKG5hLm9taXQocmF3X3BoeXNpY2lhbnMpKSksICIgQ09Ecyk6ICIsCiAgICBsZW5ndGgocmF3X3BoeXNpY2lhbnMpLAogICAgIlxuIgopKQoKIyBNb2RlbHMgY2FzZXMKcmF3X21vZGVscyA8LSByYXdfZGYgJT4lIHNlbGVjdChlbmRzX3dpdGgoIl9jZ2hyMTAiKSwgLXBoeXNpY2lhbl9jZ2hyMTApCmZvciAobWNvbCBpbiBjb2xuYW1lcyhyYXdfbW9kZWxzKSkgewogICAgbSA8LSByYXdfbW9kZWxzW1ttY29sXV0KICAgIGNhdChwYXN0ZTAoCiAgICAgICAgc3RyX3JlcGxhY2VfYWxsKG1jb2wsIG1vZGVsc19mb3JtYXQpLCAiIFByZWRpY3RlZCBDYXNlcyAoIiwgCiAgICAgICAgbGVuZ3RoKHVuaXF1ZShuYS5vbWl0KG0pKSksICIgQ09Ecyk6ICIsCiAgICAgICAgbGVuZ3RoKG0pIC0gc3VtKGlzLm5hKG0pIHwgaXMubnVsbChtKSksCiAgICAgICAgIlxuIgogICAgKSkKfQoKY2F0KCJcblByZXBhcmVkIERhdGFcbi0tLS0tLS0tLS0tLS0tXG4iKQoKIyBQaHlzaWNpYW4gY2FzZXMKcGh5c2ljaWFucyA8LSBkZiRwaHlzaWNpYW5fY2docjEwCmNhdChwYXN0ZTAoCiAgICAiUGh5c2ljaWFuIENvZGVkIENhc2VzICgiLAogICAgbGVuZ3RoKHVuaXF1ZShuYS5vbWl0KHBoeXNpY2lhbnMpKSksICIgQ09Ecyk6ICIsCiAgICBsZW5ndGgocGh5c2ljaWFucykgLSBzdW0oaXMubmEocGh5c2ljaWFucykgfCBpcy5udWxsKHBoeXNpY2lhbnMpKSwKICAgICJcbiIKKSkKCiMgTW9kZWxzIGNhc2VzCm1vZGVscyA8LSBkZiAlPiUgc2VsZWN0KGVuZHNfd2l0aCgiX2NnaHIxMCIpLCAtcGh5c2ljaWFuX2NnaHIxMCkKZm9yIChtY29sIGluIGNvbG5hbWVzKG1vZGVscykpIHsKICAgIG0gPC0gbW9kZWxzW1ttY29sXV0KICAgIGNhdChwYXN0ZTAoCiAgICAgICAgc3RyX3JlcGxhY2VfYWxsKG1jb2wsIG1vZGVsc19mb3JtYXQpLCAiIFByZWRpY3RlZCBDYXNlcyAoIiwgCiAgICAgICAgbGVuZ3RoKHVuaXF1ZShuYS5vbWl0KG0pKSksICIgQ09Ecyk6ICIsCiAgICAgICAgbGVuZ3RoKG0pIC0gc3VtKGlzLm5hKG0pIHwgaXMubnVsbChtKSksCiAgICAgICAgIlxuIgogICAgKSkKfQpgYGAKCiMjIE1ldHJpY3MKCkNhbGN1bGF0ZSBtZXRyaWNzIGZyb20gbW9kZWwgb3V0cHV0cyBjb21wYXJlZCB0byBwaHlzaWNpYW4gY29kZXMuCgpgYGB7cn0KbWV0cmljcyA8LSBsaXN0KCkKYGBgCgojIyMgUENDQwoKQ2FsY3VsYXRlIFBhcnRpYWwgQ2hhbmNlIENvcnJlY3RlZCBDb25jb3JkYW5jZSAoUENDQykgdG8gZXZhbHVhdGUgaW5kaXZ1ZGFsIHBlcmZvcm1hbmNlIGZvciBlYWNoIG1vZGVsIGJ5IGFnZSBhbmQgcGh5c2ljaWFuIGNvZGluZyBzdGFnZS4KCmBgYHtyfQoKIyBHZXQgbnVtIG9mIHVuaXF1ZSBjYXVzZXMKbmNhdXNlcyA8LSBkZiAlPiUKICAgIHNlbGVjdChlbmRzX3dpdGgoIl9jZ2hyMTAiKSkgJT4lCiAgICBwaXZvdF9sb25nZXIoCiAgICAgICAgZXZlcnl0aGluZygpLAogICAgICAgIG5hbWVzX3RvID0gImNvbHVtbiIsCiAgICAgICAgdmFsdWVzX3RvID0gImNvZCIKICAgICkgJT4lCiAgICBkaXN0aW5jdChjb2QpICU+JQogICAgZmlsdGVyKCFpcy5uYShjb2QpKSAlPiUKICAgIHB1bGwoY29kKSAlPiUKICAgIGxlbmd0aAoKIyBDYWxjIHBjY2MgZm9yIGFnZSwgc3RhZ2UsIGFuZCBjb2QKbWV0cmljcyRwY2NjIDwtIGNhbGNfYnkoZGYsIGNhbGNfcGNjYywgIlBDQ0MiLCBOID0gbmNhdXNlcykKbWV0cmljcyRwY2NjCmBgYAoKIyMjIENTTUYgQWNjdXJhY3kKCkNhbGN1bGF0ZSBDYXVzZSBTcGVjaWZpYyBNb3J0YWxpdHkgRnJhY3Rpb24gKENTTUYpIEFjY3VyYWN5IHRvIGV2YWx1YXRlIHBvcHVsYXRpb24gcGVyZm9ybWFuY2UgZm9yIGVhY2ggbW9kZWwgYnkgYWdlIGFuZCBwaHlzaWNpYW4gY29kaW5nIHN0YWdlLgoKYGBge3J9Cm1ldHJpY3MkY3NtZl9hY2MgPC0gY2FsY19ieSgKICAgIGRmLAogICAgY2FsY19jc21mX2FjYywKICAgICJDU01GIEFjY3VyYWN5IiwKICAgIGJ5X2NvZCA9IEZBTFNFCikKbWV0cmljcyRjc21mX2FjYwpgYGAKCiMjIFBsb3RzCgpTYXZlIGNhbGN1bGF0ZWQgbWV0cmljcyB0byBgZGF0YWAgZm9sZGVyLgoKYGBge3J9Cm91dCA8LSByZWR1Y2UobWV0cmljcywKICAgICAgICAgICAgICBmdW5jdGlvbih4LCB5KSBsZWZ0X2pvaW4oeCwgeSwgYnkgPSAiTW9kZWwiKQopCndyaXRlX2NzdihvdXQsICIuLi9kYXRhL2hlYWxzbF9yZDF0bzJfbWV0cmljc192MS5jc3YiKQpgYGAKCiMjIyBFeHBsb3JhdGlvbgoKYGBge3J9CgojIE51bSBhbmQgcGVyYyBvZiBhZ3JlZWQgY2FzZXMKbmFsbCA8LSBucm93KGRmKQpuYWdyZWUgPC0gbnJvdyhkZiAlPiUgZmlsdGVyKGlzX2FncmVlZCA9PSBUUlVFKSkKcGFncmVlIDwtIChuYWdyZWUgLyBuYWxsKSAqIDEwMAoKIyBDcmVhdGUgbGFiZWxzIHRvIGF4aXMgdGl0bGVzCnlfdGl0bGUgPC0gcGFzdGUwKCJQaHlzaWNpYW4gQWdyZWVkIFJlY29yZHMgKG49IiwgbmFncmVlLCAiLCAxMDAlKSIpCmBgYAoKIyMjIyBDYXNlcyBieSBTZXggYW5kIEFnZSBHcm91cAoKRGlzcGxheSBhIHBsb3Qgb2YgY2FzZXMgYnkgYWdlIGdyb3VwIHNlcGFyYXRlZCBieSBzZXguCgpgYGB7cn0KCiMgQ2FsYyBhZ2UgYW5kIHNleCBjb3VudHMKbmFkdWx0IDwtIG5yb3coZGYgJT4lIGZpbHRlcihhZ2UgPT0gImFkdWx0IiAmIGlzX2FncmVlZCA9PSBUUlVFKSkKbmNoaWxkIDwtIG5yb3coZGYgJT4lIGZpbHRlcihhZ2UgPT0gImNoaWxkIiAmIGlzX2FncmVlZCA9PSBUUlVFKSkKbm5lbyA8LSBucm93KGRmICU+JSBmaWx0ZXIoYWdlID09ICJuZW8iICYgaXNfYWdyZWVkID09IFRSVUUpKQoKIyBDYWxjIGFnZSBhbmQgc2V4IHBlcmMKcGFkdWx0IDwtIChuYWR1bHQgLyBuYWdyZWUpICogMTAwCnBjaGlsZCA8LSAobmNoaWxkIC8gbmFncmVlKSAqIDEwMApwbmVvIDwtIChubmVvIC8gbmFncmVlKSAqIDEwMAoKIyBDcmVhdGUgYWdlIHdpdGggY291bnRzCmFnZV9yZW1hcCA8LSBjKAogICAgIkFkdWx0IiA9IHBhc3RlMCgiQWR1bHQsIDEyKyB5ZWFyc1xuKG49IiwgbmFkdWx0LCAiLCAiLCByb3VuZChwYWR1bHQpLCAiJSkiKSwKICAgICJDaGlsZCIgPSBwYXN0ZTAoIkNoaWxkLCAyOCBkYXlzIHRvIDExIHllYXJzXG4obj0iLCBuY2hpbGQsICIsICIsIHJvdW5kKHBjaGlsZCksICIlKSIpLAogICAgIk5lbyIgPSBwYXN0ZTAoIk5lb25hdGFsLCA8MjggZGF5c1xuKG49Iiwgbm5lbywgIiwgIiwgcm91bmQocG5lbyksICIlKSIpCikKCiMgRm9ybWF0IHRoZSBkYXRhIGZvciBwbG90dGluZwphc2RhdGEgPC0gZGYgJT4lCiAgICBmaWx0ZXIoaXNfYWdyZWVkID09IFRSVUUpICU+JQogICAgZ3JvdXBfYnkoYWdlLCBzZXgpICU+JQogICAgc3VtbWFyaXplKGNvdW50ID0gbigpKSAlPiUKICAgIG11dGF0ZSgKICAgICAgICBhZ2UgPSBzdHJfdG9fdGl0bGUoYWdlKSwKICAgICAgICBzZXggPSBwYXN0ZTAoCiAgICAgICAgICAgIHNleCwgIiAobj0iLCBjb3VudCwgIiwgIiwKICAgICAgICAgICAgcm91bmQoY291bnQgLyBjYXNlX3doZW4oCiAgICAgICAgICAgICAgICBhZ2UgPT0gIkFkdWx0IiB+IG5hZHVsdCwKICAgICAgICAgICAgICAgIGFnZSA9PSAiQ2hpbGQiIH4gbmNoaWxkLAogICAgICAgICAgICAgICAgYWdlID09ICJOZW8iIH4gbm5lbwogICAgICAgICAgICApICogMTAwKSwgIiUpIgogICAgICAgICkKICAgICkgJT4lCiAgICBtdXRhdGUoICMgcmUtb3JkZXIgYWdlCiAgICAgICAgYWdlID0gZmFjdG9yKGFnZSwgbGV2ZWxzID0gYygKICAgICAgICAgICAgIkFkdWx0IiwKICAgICAgICAgICAgIkNoaWxkIiwKICAgICAgICAgICAgIk5lbyIKICAgICAgICApKQogICAgKSAlPiUKICAgIG11dGF0ZSggIyByZW5hbWUgYWdlcyB3aXRoIGNvdW50cwogICAgICAgIGFnZSA9IHJlY29kZShhZ2UsICEhIWFnZV9yZW1hcCkKICAgICkKCiMgUGxvdCB0aGUgZGF0YQphc3Bsb3QgPC0gYXNkYXRhICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKGFnZSksIHkgPSBjb3VudCwgZmlsbCA9IHNleCkpICsKICAgIGdlb21fYmFyKAogICAgICAgIHN0YXQgPSAiaWRlbnRpdHkiLAogICAgICAgIGZpbGwgPSAid2hpdGUiLAogICAgICAgIGNvbG9yID0gImJsYWNrIgogICAgKSArCiAgICBnZW9tX3RleHQoCiAgICAgICAgYWVzKGxhYmVsID0gc2V4KSwKICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwKICAgICAgICBzaXplID0gMi41LAogICAgICAgIGNvbG9yID0gImJsYWNrIgogICAgKSArCiAgICBsYWJzKAogICAgICAgIHkgPSB5X3RpdGxlLAogICAgICAgIHggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgZmlsbCA9ICJTZXgiCiAgICApICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZSgKICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApKQogICAgKQoKIyBTYXZlIHRoZSBwbG90CmFzcGxvdApnZ3NhdmUoIi4uL21hbnVzY3JpcHQvZmlnLWRhdGEtYWdlc2V4LnBkZiIsIHBsb3QgPSBhc3Bsb3QsIGRwaSA9IDMwMCkKYGBgCgojIyMjIENhc2VzIGJ5IEFnZSBSYW5nZQoKRGlzcGxheSBhIHBsb3QgZm9yIGVhY2ggYWdlIGdyb3VwIG9mIGNhc2VzIGJ5IGFnZSByYW5nZS4KCmBgYHtyfQpmb3IgKGEgaW4gYygiYWR1bHQiLCAiY2hpbGQiLCAibmVvIikpIHsKICAgIAogICAgIyBDcmVhdGUgbGFiZWwgZm9yIGFnZSBncm91cAogICAgYXJvd3MgPC0gZGYgJT4lIGZpbHRlcihpc19hZ3JlZWQgPT0gVFJVRSAmIGFnZSA9PSBhKSAlPiUgbnJvdwogICAgYWxhYmVsIDwtIGlmIChhID09ICJuZW8iKSAiTmVvbmF0YWwiIGVsc2Ugc3RyX3RvX3RpdGxlKGEpCiAgICBhdGl0bGUgPC0gcGFzdGUwKCJQaHlzaWNpYW4gQWdyZWVkICIsIGFsYWJlbCwgIiBSZWNvcmRzIChuPSIsIGFyb3dzLCAiLCAxMDAlKSIpCiAgICAKICAgICMgRm9ybWF0IHBsb3QgZGF0YQogICAgZWFyZGF0YSA8LSBkZiAlPiUKICAgICAgICBmaWx0ZXIoaXNfYWdyZWVkID09IFRSVUUgJiBhZ2UgPT0gYSkgJT4lCiAgICAgICAgZ3JvdXBfYnkoYWdlX3JhbmdlKSAlPiUKICAgICAgICBzdW1tYXJpemUoY291bnQgPSBuKCkpICU+JQogICAgICAgIHNlcGFyYXRlKAogICAgICAgICAgICBhZ2VfcmFuZ2UsCiAgICAgICAgICAgIGludG8gPSBjKCJhZ2VfcmFuZ2VfdmFsdWUiLCAiYWdlX3JhbmdlX3VuaXQiKSwKICAgICAgICAgICAgc2VwID0gIiAiCiAgICAgICAgKSAlPiUKICAgICAgICBzZXBhcmF0ZSgKICAgICAgICAgICAgYWdlX3JhbmdlX3ZhbHVlLAogICAgICAgICAgICBpbnRvID0gYygiYWdlX3JhbmdlX21pbiIsICJhZ2VfcmFuZ2VfbWF4IiksCiAgICAgICAgICAgIHNlcCA9ICItIgogICAgICAgICkgJT4lCiAgICAgICAgbXV0YXRlKAogICAgICAgICAgICBhZ2VfcmFuZ2VfbWluID0gYXMuaW50ZWdlcihhZ2VfcmFuZ2VfbWluKSwKICAgICAgICAgICAgYWdlX3JhbmdlX21heCA9IGFzLmludGVnZXIoYWdlX3JhbmdlX21heCksCiAgICAgICAgICAgIGFnZV9yYW5nZV91bml0ID0gZmFjdG9yKGFnZV9yYW5nZV91bml0LCBsZXZlbHMgPSBjKAogICAgICAgICAgICAgICAgIldlZWtzIiwKICAgICAgICAgICAgICAgICJEYXlzIiwKICAgICAgICAgICAgICAgICJNb250aHMiLAogICAgICAgICAgICAgICAgIlllYXJzIgogICAgICAgICAgICApKSwKICAgICAgICAgICAgYWdlX3JhbmdlX2xhYmVsID0gcGFzdGUwKAogICAgICAgICAgICAgICAgYWdlX3JhbmdlX21pbiwKICAgICAgICAgICAgICAgICItIiwKICAgICAgICAgICAgICAgIGFnZV9yYW5nZV9tYXgsCiAgICAgICAgICAgICAgICAiICIsCiAgICAgICAgICAgICAgICBhZ2VfcmFuZ2VfdW5pdCwKICAgICAgICAgICAgICAgICJcbihuPSIsIGNvdW50LCAiLCAiLCByb3VuZCgoY291bnQgLyBhcm93cykgKiAxMDApLCAiJSkiCiAgICAgICAgICAgICkKICAgICAgICApICU+JQogICAgICAgIGFycmFuZ2UoYWdlX3JhbmdlX3VuaXQsIGFnZV9yYW5nZV9taW4sIGFnZV9yYW5nZV9tYXgpICU+JQogICAgICAgIG11dGF0ZSgKICAgICAgICAgICAgYWdlX3JhbmdlX2xhYmVsID0gZmFjdG9yKGFnZV9yYW5nZV9sYWJlbCwgbGV2ZWxzID0gdW5pcXVlKGFnZV9yYW5nZV9sYWJlbCkpCiAgICAgICAgKQogICAgCiAgICAjIFBsb3QgdGhlIGRhdGEKICAgIGVhcnBsb3QgPC0gZWFyZGF0YSAlPiUKICAgICAgICBnZ3Bsb3QoYWVzKHggPSBhZ2VfcmFuZ2VfbGFiZWwsIHkgPSBjb3VudCkpICsKICAgICAgICBnZW9tX2JhcigKICAgICAgICAgICAgc3RhdCA9ICJpZGVudGl0eSIsCiAgICAgICAgICAgIGZpbGwgPSAiIzFkMWQxZCIsCiAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwKICAgICAgICAgICAgd2lkdGggPSAwLjUKICAgICAgICApICsKICAgICAgICBsYWJzKAogICAgICAgICAgICB5ID0gYXRpdGxlLAogICAgICAgICAgICB4ID0gZWxlbWVudF9ibGFuaygpCiAgICAgICAgKSArCiAgICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgICB0aGVtZSgKICAgICAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gMTApKSwKICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApKQogICAgICAgICkgKwogICAgICAgIGNvb3JkX2ZsaXAoKQogICAgCiAgICAjIFNhdmUgdGhlIHBsb3QKICAgIHByaW50KGVhcnBsb3QpCiAgICBnZ3NhdmUoCiAgICAgICAgc3ByaW50ZigiLi4vbWFudXNjcmlwdC9maWctZGF0YS1hZ2VyYW5nZS0lcy5wZGYiLCBhKSwKICAgICAgICBwbG90ID0gZWFycGxvdCwKICAgICAgICBkcGkgPSAzMDAKICAgICkKfQpgYGAKCiMjIyMgQ2FzZXMgYnkgQ09ECgpEaXNwbGF5IGEgcGxvdCBmb3IgZWFjaCBhZ2UgZ3JvdXAgb2YgY2FzZXMgYnkgY2F1c2Ugb2YgZGVhdGguCgpgYGB7cn0KZm9yIChhIGluIGMoImFkdWx0IiwgImNoaWxkIiwgIm5lbyIpKSB7CiAgICAKICAgICMgQ3JlYXRlIGxhYmVsIGZvciBhZ2UgZ3JvdXAKICAgIGFyb3dzIDwtIGRmICU+JSBmaWx0ZXIoaXNfYWdyZWVkID09IFRSVUUgJiBhZ2UgPT0gYSkgJT4lIG5yb3cKICAgIGFsYWJlbCA8LSBpZiAoYSA9PSAibmVvIikgIk5lb25hdGFsIiBlbHNlIHN0cl90b190aXRsZShhKQogICAgYXRpdGxlIDwtIHBhc3RlMCgiUGh5c2ljaWFuIEFncmVlZCAiLCBhbGFiZWwsICIgUmVjb3JkcyAobj0iLCBhcm93cywgIiwgMTAwJSkiKQogICAgCiAgICAjIEZvcm1hdCBwbG90IGRhdGEKICAgIGVjb2RkYXRhIDwtIGRmICU+JQogICAgICAgIGZpbHRlcihpc19hZ3JlZWQgPT0gVFJVRSAmIGFnZSA9PSBhKSAlPiUKICAgICAgICBncm91cF9ieShwaHlzaWNpYW5fY2docjEwKSAlPiUKICAgICAgICBzdW1tYXJpemUoY291bnQgPSBuKCkpICU+JQogICAgICAgIG11dGF0ZSgKICAgICAgICAgICAgY29kX2xhYmVsID0gcGFzdGUwKAogICAgICAgICAgICAgICAgaWZfZWxzZSgKICAgICAgICAgICAgICAgICAgICBzdHJfY291bnQocGh5c2ljaWFuX2NnaHIxMCwgIlxccysiKSA+IDMsCiAgICAgICAgICAgICAgICAgICAgc3RyX3JlcGxhY2UocGh5c2ljaWFuX2NnaHIxMCwgIihcXFMrXFxzK1xcUytcXHMrXFxTKykgIiwgIlxcMVxuIiksCiAgICAgICAgICAgICAgICAgICAgcGh5c2ljaWFuX2NnaHIxMAogICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICJcbihuPSIsIGNvdW50LCAiLCAiLAogICAgICAgICAgICAgICAgaWZfZWxzZSgKICAgICAgICAgICAgICAgICAgICAoKGNvdW50IC8gYXJvd3MpICogMTAwKSA8IDEsCiAgICAgICAgICAgICAgICAgICAgIjwxIiwKICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIocm91bmQoKGNvdW50IC8gYXJvd3MpICogMTAwKSkKICAgICAgICAgICAgICAgICksICIlKSIKICAgICAgICAgICAgKQogICAgICAgICkgJT4lCiAgICAgICAgYXJyYW5nZShjb3VudCkgJT4lCiAgICAgICAgbXV0YXRlKAogICAgICAgICAgICBjb2RfbGFiZWwgPSBmYWN0b3IoY29kX2xhYmVsLCBsZXZlbHMgPSB1bmlxdWUoY29kX2xhYmVsKSkKICAgICAgICApCiAgICAKICAgICMgUGxvdCB0aGUgZGF0YQogICAgZWNvZHBsb3QgPC0gZWNvZGRhdGEgJT4lCiAgICAgICAgZ2dwbG90KGFlcyh4ID0gY29kX2xhYmVsLCB5ID0gY291bnQpKSArCiAgICAgICAgZ2VvbV9iYXIoCiAgICAgICAgICAgIHN0YXQgPSAiaWRlbnRpdHkiLAogICAgICAgICAgICBmaWxsID0gIiMxZDFkMWQiLAogICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsCiAgICAgICAgICAgIHdpZHRoID0gMC41CiAgICAgICAgKSArCiAgICAgICAgbGFicygKICAgICAgICAgICAgeSA9IGF0aXRsZSwKICAgICAgICAgICAgeCA9IGVsZW1lbnRfYmxhbmsoKQogICAgICAgICkgKwogICAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgICAgdGhlbWUoCiAgICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksCiAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSkKICAgICAgICApICsKICAgICAgICBjb29yZF9mbGlwKCkKICAgIAogICAgIyBTYXZlIHRoZSBwbG90CiAgICBwcmludChlY29kcGxvdCkKICAgIGdnc2F2ZSgKICAgICAgICBzcHJpbnRmKCIuLi9tYW51c2NyaXB0L2ZpZy1kYXRhLWNvZC0lcy5wZGYiLCBhKSwKICAgICAgICBwbG90ID0gZWNvZHBsb3QsCiAgICAgICAgZHBpID0gMzAwLAogICAgICAgIHdpZHRoID0gaWYgKGEgPT0gImFkdWx0IikgNiBlbHNlIE5BLAogICAgICAgIGhlaWdodCA9IGlmIChhID09ICJhZHVsdCIpIDggZWxzZSBOQQogICAgKQp9CmBgYAoKIyMjIFBlcmZvcm1hbmNlCgojIyMjIEFsbCB2cyBBZ3JlZSBQZXJmb3JtYW5jZQoKRGlzcGxheSBhIHBsb3Qgb2YgbW9kZWwgcGVyZm9ybWFuY2UgZm9yIGFsbCByZWNvcmRzIHZlcnN1cyByZWNvcmRzIHdoZXJlIHBoeXNpY2lhbnMgYWdyZWVkIG9uIHRoZSBDT0QgY29kZS4KCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMi41LCBmaWcud2lkdGggPSA2fQoKIyBDYWxjIHN0YWdlIGNvdW50cwpuYWxsIDwtIG5yb3coZGYpCm5hZ3JlZSA8LSBucm93KGRmICU+JSBmaWx0ZXIoaXNfYWdyZWVkID09IFRSVUUpKQpucmVjb24gPC0gbnJvdyhkZiAlPiUgZmlsdGVyKGlzX3JlY29uID09IFRSVUUpKQpuYWRqIDwtIG5yb3coZGYgJT4lIGZpbHRlcihpc19hZGogPT0gVFJVRSkpCgojIENhbGMgc3RhZ2UgcGVyYwpwYWdyZWUgPC0gKG5hZ3JlZSAvIG5hbGwpICogMTAwCnByZWNvbiA8LSAobnJlY29uIC8gbmFsbCkgKiAxMDAKcGFkaiA8LSAobmFkaiAvIG5hbGwpICogMTAwCgojIENyZWF0ZSBzdGFnZSB3aXRoIGNvdW50cwpzdGFnZV9yZW1hcCA8LSBjKAogICAgIkFsbCIgPSBwYXN0ZTAoIkFsbFxuUmVjb3Jkc1xuKG49IiwgbmFsbCwgIiwgMTAwJSkiKSwKICAgICJBZ3JlZWQiID0gcGFzdGUwKCJQaHlzaWNpYW4gQWdyZWVkXG5SZWNvcmRzXG4obj0iLCBuYWdyZWUsICIsICIsIHJvdW5kKHBhZ3JlZSksICIlKSIpLAogICAgIlJlY29uY2lsZWQiID0gcGFzdGUwKCJSZWNvbmNpbGVkXG4obj0iLCBucmVjb24sICIsICIsIHJvdW5kKHByZWNvbiksICIlKSIpLAogICAgIkFkanVkaWNhdGVkIiA9IHBhc3RlMCgiQWRqdWRpY2F0ZWRcbihuPSIsIG5hZGosICIsICIsIHJvdW5kKHBhZGopLCAiJSkiKQopCgojIFByZXBhcmUgYm94cGxvdCBkYXRhCmFsbGRhdGEgPC0gb3V0ICU+JQogICAgc2VsZWN0KAogICAgICAgIE1vZGVsLAogICAgICAgIGBQQ0NDYCwKICAgICAgICBgUENDQyBBZ3JlZW1lbnRgLAogICAgICAgIGBDU01GIEFjY3VyYWN5YCwKICAgICAgICBgQ1NNRiBBY2N1cmFjeSBBZ3JlZW1lbnRgCiAgICApICU+JQogICAgcmVuYW1lKAogICAgICAgICBBbGwgPSBgUENDQ2AsCiAgICAgICAgIEFncmVlZCA9IGBQQ0NDIEFncmVlbWVudGAKICAgICkgJT4lCiAgICBmaWx0ZXIoIXN0cl9kZXRlY3QoTW9kZWwsICImIikpICU+JSAjIHJlbW92ZSBtdWx0aSBtb2RlbHMKICAgIHBpdm90X2xvbmdlciggIyB0cmFuc2Zvcm0gdG8gbG9uZyBmb3JtYXQKICAgICAgICBjb2xzID0gLWMoCiAgICAgICAgICAgIE1vZGVsLAogICAgICAgICAgICBgQ1NNRiBBY2N1cmFjeWAsCiAgICAgICAgICAgIGBDU01GIEFjY3VyYWN5IEFncmVlbWVudGAKICAgICAgICApLAogICAgICAgIG5hbWVzX3RvID0gIlN0YWdlIiwKICAgICAgICB2YWx1ZXNfdG8gPSAiUENDQyIKICAgICkgJT4lCiAgICBncm91cF9ieShTdGFnZSkgJT4lCiAgICBtdXRhdGUoICMgQWRkIG1pbiwgbWlkLCBhbmQgbWF4IG1vZGVsIG5hbWVzIHRvIGxhYmVsCiAgICAgICAgIlBDQ0MgTWluIiA9IGlmX2Vsc2UoCiAgICAgICAgICAgIFBDQ0MgPD0gbWluKFBDQ0MpICsgMC4wNSwKICAgICAgICAgICAgcGFzdGUwKAogICAgICAgICAgICAgICAgTW9kZWwsICJcbigiLAogICAgICAgICAgICAgICAgaWZfZWxzZShyb3VuZChQQ0NDLCAyKSAhPSByb3VuZChtaW4oUENDQyksIDIpLCBwYXN0ZTAocm91bmQoUENDQywgMiksICIsICIpLCAiIiksCiAgICAgICAgICAgICAgICAiQ1NNRj0iLCByb3VuZChpZl9lbHNlKFN0YWdlID09ICJBbGwiLCBgQ1NNRiBBY2N1cmFjeWAsIGBDU01GIEFjY3VyYWN5IEFncmVlbWVudGApLCAyKSwgIikiKSwKICAgICAgICAgICAgTkEKICAgICAgICApLAogICAgICAgICJQQ0NDIE1heCIgPSBpZl9lbHNlKAogICAgICAgICAgICBQQ0NDID49IG1heChQQ0NDKSAtIDAuMDUsCiAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgIE1vZGVsLCAiXG4oIiwKICAgICAgICAgICAgICAgIGlmX2Vsc2Uocm91bmQoUENDQywgMikgIT0gcm91bmQobWF4KFBDQ0MpLCAyKSwgcGFzdGUwKHJvdW5kKFBDQ0MsIDIpLCAiLCAiKSwgIiIpLAogICAgICAgICAgICAgICAgIkNTTUY9Iiwgcm91bmQoaWZfZWxzZShTdGFnZSA9PSAiQWxsIiwgYENTTUYgQWNjdXJhY3lgLCBgQ1NNRiBBY2N1cmFjeSBBZ3JlZW1lbnRgKSwgMiksICIpIiksCiAgICAgICAgICAgIE5BCiAgICAgICAgKSwKICAgICAgICAiUENDQyBNaWQiID0gaWZfZWxzZSgKICAgICAgICAgICAgaXMubmEoYFBDQ0MgTWluYCkgJiBpcy5uYShgUENDQyBNYXhgKSwKICAgICAgICAgICAgcGFzdGUwKE1vZGVsLCAiXG4oIiwgcm91bmQoUENDQywgMiksICIsIENTTUY9Iiwgcm91bmQoaWZfZWxzZShTdGFnZSA9PSAiQWxsIiwgYENTTUYgQWNjdXJhY3lgLCBgQ1NNRiBBY2N1cmFjeSBBZ3JlZW1lbnRgKSwgMiksICIpIiksCiAgICAgICAgICAgIE5BCiAgICAgICAgKSwKICAgICAgICAiUENDQyBNaWQgVmFsdWUiID0gaWZfZWxzZSgKICAgICAgICAgICAgaXMubmEoYFBDQ0MgTWluYCkgJiBpcy5uYShgUENDQyBNYXhgKSwKICAgICAgICAgICAgUENDQywKICAgICAgICAgICAgTkEKICAgICAgICApLAogICAgICAgICJQQ0NDIE1pbiIgPSBpZl9lbHNlKCAjIENvbWJpbmUgaW50byBvbmUgcm93IGlmIGNsb3NlIFBDQ0MKICAgICAgICAgICAgYFBDQ0NgID09IG1pbihQQ0NDKSwKICAgICAgICAgICAgaWZfZWxzZSgKICAgICAgICAgICAgICAgIHN1bSghaXMubmEoYFBDQ0MgTWluYCkpID4gMSwKICAgICAgICAgICAgICAgIHBhc3RlMChuYS5vbWl0KGBQQ0NDIE1pbmApLCBjb2xsYXBzZSA9ICJcbiIpLAogICAgICAgICAgICAgICAgcGFzdGUwKAogICAgICAgICAgICAgICAgICAgIHN0cl9yZXBsYWNlKG5hLm9taXQoYFBDQ0MgTWluYCksICJcXHMqXFwoLipcXCxcXHMqIiwgIlxuKCIpLAogICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIlxuIgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICApLAogICAgICAgICAgICBOQQogICAgICAgICksCiAgICAgICAgIlBDQ0MgTWF4IiA9IGlmX2Vsc2UoCiAgICAgICAgICAgIGBQQ0NDYCA9PSBtYXgoUENDQyksCiAgICAgICAgICAgIGlmX2Vsc2UoCiAgICAgICAgICAgICAgICBzdW0oIWlzLm5hKGBQQ0NDIE1heGApKSA+IDEsCiAgICAgICAgICAgICAgICBwYXN0ZTAobmEub21pdChgUENDQyBNYXhgKSwgY29sbGFwc2UgPSAiXG4iKSwKICAgICAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgICAgICBzdHJfcmVwbGFjZShuYS5vbWl0KGBQQ0NDIE1heGApLCAiXFxzKlxcKC4qXFwsXFxzKiIsICJcbigiKSwKICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICJcbiIKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKSwKICAgICAgICAgICAgTkEKICAgICAgICApCiAgICApICU+JQogICAgbXV0YXRlKCAjIHJlLW9yZGVyIHN0YWdlCiAgICAgICAgU3RhZ2UgPSBmYWN0b3IoU3RhZ2UsIGxldmVscyA9IGMoCiAgICAgICAgICAgICJBZ3JlZWQiLAogICAgICAgICAgICAiQWxsIgogICAgICAgICkpCiAgICApICU+JQogICAgbXV0YXRlKCAjIHJlbmFtZSBzdGFnZXMgd2l0aCBjb3VudHMKICAgICAgICBTdGFnZSA9IHJlY29kZShTdGFnZSwgISEhc3RhZ2VfcmVtYXApCiAgICApCgojIFBsb3QgYm94cGxvdAphbGxwbG90IDwtIGFsbGRhdGEgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBTdGFnZSwgeSA9IFBDQ0MpKSArCiAgICBnZW9tX2JveHBsb3QoCiAgICAgICAgbGluZXdpZHRoID0gMC41LAogICAgICAgIHdpZHRoID0gMC4yNQogICAgKSArCiAgICBnZW9tX3BvaW50KAogICAgICAgIGFlcyh5ID0gYFBDQ0MgTWlkIFZhbHVlYCksCiAgICAgICAgc2hhcGUgPSAxLAogICAgICAgIHNpemUgPSAxLjUsCiAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsCiAgICAgICAgY29sb3IgPSAiZGFya2dyYXkiLAogICAgICAgIGFscGhhID0gMC44LAogICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeCA9IC0wLjMpCiAgICApICsKICAgIGdlb21fdGV4dCgKICAgICAgICBhZXMobGFiZWwgPSBgUENDQyBNaW5gKSwKICAgICAgICBjb2xvciA9ICIjNGQ0ZDRkIiwKICAgICAgICBzaXplID0gMiwKICAgICAgICBoanVzdCA9IDEsCiAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh5ID0gLTAuMDU1KQogICAgKSArCiAgICBnZW9tX3RleHQoCiAgICAgICAgYWVzKGxhYmVsID0gYFBDQ0MgTWF4YCksCiAgICAgICAgY29sb3IgPSAiIzRkNGQ0ZCIsCiAgICAgICAgc2l6ZSA9IDIsCiAgICAgICAgaGp1c3QgPSAwLAogICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeSA9IDAuMDU1KQogICAgKSArCiAgICBnZW9tX3RleHQoCiAgICAgICAgYWVzKGxhYmVsID0gYFBDQ0MgTWlkYCksCiAgICAgICAgY29sb3IgPSAiIzRkNGQ0ZCIsCiAgICAgICAgc2l6ZSA9IDIsCiAgICAgICAgaGp1c3QgPSAxLAogICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeSA9IC0wLjAxNSwgeCA9IC0wLjMpCiAgICApICsKICAgIHN0YXRfc3VtbWFyeSggIyBtaW4gcGNjYyB0eHQgb24gYm94cGxvdAogICAgICAgIGdlb20gPSAidGV4dCIsCiAgICAgICAgZnVuID0gbWluLAogICAgICAgIGFlcyhsYWJlbCA9IHNwcmludGYoIiUxLjJmIiwgYWZ0ZXJfc3RhdCh5KSkpLAogICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeSA9IC0wLjAyNSksCiAgICAgICAgc2l6ZSA9IDMKICAgICkgKwogICAgc3RhdF9zdW1tYXJ5KCAjIG1heCBwY2NjIHR4dCBvbiBib3hwbG90CiAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICBmdW4gPSBtYXgsCiAgICAgICAgYWVzKGxhYmVsID0gc3ByaW50ZigiJTEuMmYiLCBhZnRlcl9zdGF0KHkpKSksCiAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh5ID0gMC4wMjUpLAogICAgICAgIHNpemUgPSAzCiAgICApICsKICAgIGxhYnMoCiAgICAgICAgeCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICB5ID0gIlBDQ0MgKDA9TG93LCAxPUhpZ2gpIgogICAgKSArCiAgICB5bGltKDAuMiwgMC44NSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZSgKICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApKSwKICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZSgKICAgICAgICAgICAgYXJyb3cgPSBncmlkOjphcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImNtIiksIGVuZHMgPSAiYm90aCIpCiAgICAgICAgKSwKICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKQogICAgKQoKIyBTYXZlIHRoZSBwbG90CmFsbHBsb3QKZ2dzYXZlKCIuLi9tYW51c2NyaXB0L2ZpZy1wZXJmLWFsbHZzYWdyZWUucGRmIiwgcGxvdCA9IGFsbHBsb3QsIGRwaSA9IDMwMCkKYGBgCgojIyMjIEFnZSBHcm91cCBQZXJmb21hbmNlCgpEaXNwbGF5IGEgcGxvdCBvZiBwZXJmb3JtYW5jZSBmb3IgcGh5c2ljaWFuIGFncmVlZCByZWNvcmRzIGJ5IGFnZSBncm91cC4KCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMy41LCBmaWcud2lkdGggPSA2fQoKIyBDYWxjIGFnZSBjb3VudHMKbmFncmVlIDwtIG5yb3coZGYgJT4lIGZpbHRlcihpc19hZ3JlZWQgPT0gVFJVRSkpCm5hZHVsdCA8LSBucm93KGRmICU+JSBmaWx0ZXIoYWdlID09ICJhZHVsdCIgJiBpc19hZ3JlZWQgPT0gVFJVRSkpCm5jaGlsZCA8LSBucm93KGRmICU+JSBmaWx0ZXIoYWdlID09ICJjaGlsZCIgJiBpc19hZ3JlZWQgPT0gVFJVRSkpCm5uZW8gPC0gbnJvdyhkZiAlPiUgZmlsdGVyKGFnZSA9PSAibmVvIiAmIGlzX2FncmVlZCA9PSBUUlVFKSkKCiMgQ2FsYyBhZ2UgcGVyYwpwYWR1bHQgPC0gKG5hZHVsdCAvIG5hZ3JlZSkgKiAxMDAKcGNoaWxkIDwtIChuY2hpbGQgLyBuYWdyZWUpICogMTAwCnBuZW8gPC0gKG5uZW8gLyBuYWdyZWUpICogMTAwCgojIENyZWF0ZSBhZ2Ugd2l0aCBjb3VudHMKYWdlX3JlbWFwIDwtIGMoCiAgICAiQWR1bHQiID0gcGFzdGUwKCJBZHVsdFxuMTIrIHllYXJzXG4obj0iLCBuYWR1bHQsICIsICIsIHJvdW5kKHBhZHVsdCksICIlKSIpLAogICAgIkNoaWxkIiA9IHBhc3RlMCgiQ2hpbGRcbjI4IGRheXMgdG8gMTEgeWVhcnNcbihuPSIsIG5jaGlsZCwgIiwgIiwgcm91bmQocGNoaWxkKSwgIiUpIiksCiAgICAiTmVvbmF0YWwiID0gcGFzdGUwKCJOZW9uYXRhbFxuPDI4IGRheXNcbihuPSIsIG5uZW8sICIsICIsIHJvdW5kKHBuZW8pLCAiJSkiKQopCgojIFByZXBhcmUgYm94cGxvdCBkYXRhCmFnZWRhdGEgPC0gb3V0ICU+JQogICAgc2VsZWN0KAogICAgICAgIE1vZGVsLAogICAgICAgIGBQQ0NDIEFkdWx0IEFncmVlbWVudGAsCiAgICAgICAgYFBDQ0MgQ2hpbGQgQWdyZWVtZW50YCwKICAgICAgICBgUENDQyBOZW8gQWdyZWVtZW50YCwKICAgICAgICBgQ1NNRiBBY2N1cmFjeSBBZHVsdCBBZ3JlZW1lbnRgLAogICAgICAgIGBDU01GIEFjY3VyYWN5IENoaWxkIEFncmVlbWVudGAsCiAgICAgICAgYENTTUYgQWNjdXJhY3kgTmVvIEFncmVlbWVudGAKICAgICkgJT4lCiAgICByZW5hbWUoCiAgICAgICAgIEFkdWx0ID0gYFBDQ0MgQWR1bHQgQWdyZWVtZW50YCwKICAgICAgICAgQ2hpbGQgPSBgUENDQyBDaGlsZCBBZ3JlZW1lbnRgLAogICAgICAgICBOZW9uYXRhbCA9IGBQQ0NDIE5lbyBBZ3JlZW1lbnRgCiAgICApICU+JQogICAgZmlsdGVyKCFzdHJfZGV0ZWN0KE1vZGVsLCAiJiIpKSAlPiUgIyByZW1vdmUgbXVsdGkgbW9kZWxzCiAgICBwaXZvdF9sb25nZXIoICMgdHJhbnNmb3JtIHRvIGxvbmcgZm9ybWF0CiAgICAgICAgY29scyA9IC1jKAogICAgICAgICAgICBNb2RlbCwKICAgICAgICAgICAgYENTTUYgQWNjdXJhY3kgQWR1bHQgQWdyZWVtZW50YCwKICAgICAgICAgICAgYENTTUYgQWNjdXJhY3kgQ2hpbGQgQWdyZWVtZW50YCwKICAgICAgICAgICAgYENTTUYgQWNjdXJhY3kgTmVvIEFncmVlbWVudGAKICAgICAgICApLAogICAgICAgIG5hbWVzX3RvID0gIkFnZSBHcm91cCIsCiAgICAgICAgdmFsdWVzX3RvID0gIlBDQ0MiCiAgICApICU+JQogICAgZ3JvdXBfYnkoYEFnZSBHcm91cGApICU+JQogICAgbXV0YXRlKCAjIEFkZCBtaW4sIG1pZCwgYW5kIG1heCBtb2RlbCBuYW1lcyB0byBsYWJlbAogICAgICAgICJQQ0NDIE1pbiIgPSBpZl9lbHNlKAogICAgICAgICAgICBQQ0NDIDw9IG1pbihQQ0NDKSArIDAuMDUsCiAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgIE1vZGVsLCAiXG4oIiwKICAgICAgICAgICAgICAgIGlmX2Vsc2Uocm91bmQoUENDQywgMikgIT0gcm91bmQobWluKFBDQ0MpLCAyKSwgcGFzdGUwKHJvdW5kKFBDQ0MsIDIpLCAiLCAiKSwgIiIpLAogICAgICAgICAgICAgICAgIkNTTUY9Iiwgcm91bmQoY2FzZV93aGVuKAogICAgICAgICAgICAgICAgICAgIGBBZ2UgR3JvdXBgID09ICJBZHVsdCIgIH4gYENTTUYgQWNjdXJhY3kgQWR1bHQgQWdyZWVtZW50YCwKICAgICAgICAgICAgICAgICAgICBgQWdlIEdyb3VwYCA9PSAiQ2hpbGQiICB+IGBDU01GIEFjY3VyYWN5IENoaWxkIEFncmVlbWVudGAsCiAgICAgICAgICAgICAgICAgICAgYEFnZSBHcm91cGAgPT0gIk5lb25hdGFsIiAgfiBgQ1NNRiBBY2N1cmFjeSBOZW8gQWdyZWVtZW50YAogICAgICAgICAgICAgICAgKSwyKSwgIikiKSwKICAgICAgICAgICAgTkEKICAgICAgICApLAogICAgICAgICJQQ0NDIE1heCIgPSBpZl9lbHNlKAogICAgICAgICAgICBQQ0NDID49IG1heChQQ0NDKSAtIDAuMDUsCiAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgIE1vZGVsLCAiXG4oIiwKICAgICAgICAgICAgICAgIGlmX2Vsc2Uocm91bmQoUENDQywgMikgIT0gcm91bmQobWF4KFBDQ0MpLCAyKSwgcGFzdGUwKHJvdW5kKFBDQ0MsIDIpLCAiLCAiKSwgIiIpLAogICAgICAgICAgICAgICAgIkNTTUY9Iiwgcm91bmQoY2FzZV93aGVuKAogICAgICAgICAgICAgICAgICAgIGBBZ2UgR3JvdXBgID09ICJBZHVsdCIgIH4gYENTTUYgQWNjdXJhY3kgQWR1bHQgQWdyZWVtZW50YCwKICAgICAgICAgICAgICAgICAgICBgQWdlIEdyb3VwYCA9PSAiQ2hpbGQiICB+IGBDU01GIEFjY3VyYWN5IENoaWxkIEFncmVlbWVudGAsCiAgICAgICAgICAgICAgICAgICAgYEFnZSBHcm91cGAgPT0gIk5lb25hdGFsIiAgfiBgQ1NNRiBBY2N1cmFjeSBOZW8gQWdyZWVtZW50YAogICAgICAgICAgICAgICAgKSwyKSwgIikiKSwKICAgICAgICAgICAgTkEKICAgICAgICApLAogICAgICAgICJQQ0NDIE1pZCIgPSBpZl9lbHNlKAogICAgICAgICAgICBpcy5uYShgUENDQyBNaW5gKSAmIGlzLm5hKGBQQ0NDIE1heGApLAogICAgICAgICAgICBwYXN0ZTAoCiAgICAgICAgICAgICAgICBNb2RlbCwgIlxuKCIsIHJvdW5kKFBDQ0MsIDIpLAogICAgICAgICAgICAgICAgIiwgQ1NNRj0iLCByb3VuZChjYXNlX3doZW4oCiAgICAgICAgICAgICAgICAgICAgYEFnZSBHcm91cGAgPT0gIkFkdWx0IiAgfiBgQ1NNRiBBY2N1cmFjeSBBZHVsdCBBZ3JlZW1lbnRgLAogICAgICAgICAgICAgICAgICAgIGBBZ2UgR3JvdXBgID09ICJDaGlsZCIgIH4gYENTTUYgQWNjdXJhY3kgQ2hpbGQgQWdyZWVtZW50YCwKICAgICAgICAgICAgICAgICAgICBgQWdlIEdyb3VwYCA9PSAiTmVvbmF0YWwiICB+IGBDU01GIEFjY3VyYWN5IE5lbyBBZ3JlZW1lbnRgCiAgICAgICAgICAgICAgICApLDIpLCAiKSIpLAogICAgICAgICAgICBOQQogICAgICAgICksCiAgICAgICAgIlBDQ0MgTWlkIFZhbHVlIiA9IGlmX2Vsc2UoCiAgICAgICAgICAgIGlzLm5hKGBQQ0NDIE1pbmApICYgaXMubmEoYFBDQ0MgTWF4YCksCiAgICAgICAgICAgIFBDQ0MsCiAgICAgICAgICAgIE5BCiAgICAgICAgKSwKICAgICAgICAiUENDQyBNaW4iID0gaWZfZWxzZSggIyBDb21iaW5lIGludG8gb25lIHJvdyBpZiBjbG9zZSBQQ0NDCiAgICAgICAgICAgIGBQQ0NDYCA9PSBtaW4oUENDQyksCiAgICAgICAgICAgIGlmX2Vsc2UoCiAgICAgICAgICAgICAgICBzdW0oIWlzLm5hKGBQQ0NDIE1pbmApKSA+IDEsCiAgICAgICAgICAgICAgICBwYXN0ZTAobmEub21pdChgUENDQyBNaW5gKSwgY29sbGFwc2UgPSAiXG4iKSwKICAgICAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgICAgICBzdHJfcmVwbGFjZShuYS5vbWl0KGBQQ0NDIE1pbmApLCAiXFxzKlxcKC4qXFwsXFxzKiIsICJcbigiKSwKICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICJcbiIKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKSwKICAgICAgICAgICAgTkEKICAgICAgICApLAogICAgICAgICJQQ0NDIE1heCIgPSBpZl9lbHNlKAogICAgICAgICAgICBgUENDQ2AgPT0gbWF4KFBDQ0MpLAogICAgICAgICAgICBpZl9lbHNlKAogICAgICAgICAgICAgICAgc3VtKCFpcy5uYShgUENDQyBNYXhgKSkgPiAxLAogICAgICAgICAgICAgICAgcGFzdGUwKG5hLm9taXQoYFBDQ0MgTWF4YCksIGNvbGxhcHNlID0gIlxuIiksCiAgICAgICAgICAgICAgICBwYXN0ZTAoCiAgICAgICAgICAgICAgICAgICAgc3RyX3JlcGxhY2UobmEub21pdChgUENDQyBNYXhgKSwgIlxccypcXCguKlxcLFxccyoiLCAiXG4oIiksCiAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiXG4iCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICksCiAgICAgICAgICAgIE5BCiAgICAgICAgKQogICAgKSAlPiUKICAgIG11dGF0ZSggIyByZS1vcmRlciBhZ2UKICAgICAgICAiQWdlIEdyb3VwIiA9IGZhY3RvcihgQWdlIEdyb3VwYCwgbGV2ZWxzID0gYygKICAgICAgICAgICAgIk5lb25hdGFsIiwKICAgICAgICAgICAgIkNoaWxkIiwKICAgICAgICAgICAgIkFkdWx0IgogICAgICAgICkpCiAgICApICU+JQogICAgbXV0YXRlKCAjIHJlbmFtZSBhZ2VzIHdpdGggY291bnRzCiAgICAgICAgIkFnZSBHcm91cCIgPSByZWNvZGUoYEFnZSBHcm91cGAsICEhIWFnZV9yZW1hcCkKICAgICkKCiMgUGxvdCBib3hwbG90CmFnZXBsb3QgPC0gYWdlZGF0YSAlPiUKICAgIGdncGxvdChhZXMoeCA9IGBBZ2UgR3JvdXBgLCB5ID0gUENDQykpICsKICAgIGdlb21fYm94cGxvdCgKICAgICAgICBsaW5ld2lkdGggPSAwLjUsCiAgICAgICAgd2lkdGggPSAwLjI1CiAgICApICsKICAgIGdlb21fcG9pbnQoCiAgICAgICAgYWVzKHkgPSBgUENDQyBNaWQgVmFsdWVgKSwKICAgICAgICBzaGFwZSA9IDEsCiAgICAgICAgc2l6ZSA9IDEuNSwKICAgICAgICBmaWxsID0gIndoaXRlIiwKICAgICAgICBjb2xvciA9ICJkYXJrZ3JheSIsCiAgICAgICAgYWxwaGEgPSAwLjgsCiAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gLTAuMykKICAgICkgKwogICAgZ2VvbV90ZXh0KAogICAgICAgIGFlcyhsYWJlbCA9IGBQQ0NDIE1pbmApLAogICAgICAgIGNvbG9yID0gIiM0ZDRkNGQiLAogICAgICAgIHNpemUgPSAyLAogICAgICAgIGhqdXN0ID0gMSwKICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHkgPSAtMC4wNTUpCiAgICApICsKICAgIGdlb21fdGV4dCgKICAgICAgICBhZXMobGFiZWwgPSBgUENDQyBNYXhgKSwKICAgICAgICBjb2xvciA9ICIjNGQ0ZDRkIiwKICAgICAgICBzaXplID0gMiwKICAgICAgICBoanVzdCA9IDAsCiAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh5ID0gMC4wNTUpCiAgICApICsKICAgIGdlb21fdGV4dCgKICAgICAgICBhZXMobGFiZWwgPSBgUENDQyBNaWRgKSwKICAgICAgICBjb2xvciA9ICIjNGQ0ZDRkIiwKICAgICAgICBzaXplID0gMiwKICAgICAgICBoanVzdCA9IDEsCiAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh5ID0gLTAuMDE1LCB4ID0gLTAuMykKICAgICkgKwogICAgc3RhdF9zdW1tYXJ5KCAjIG1pbiBwY2NjIHR4dCBvbiBib3hwbG90CiAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICBmdW4gPSBtaW4sCiAgICAgICAgYWVzKGxhYmVsID0gc3ByaW50ZigiJTEuMmYiLCBhZnRlcl9zdGF0KHkpKSksCiAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh5ID0gLTAuMDI1KSwKICAgICAgICBzaXplID0gMwogICAgKSArCiAgICBzdGF0X3N1bW1hcnkoICMgbWF4IHBjY2MgdHh0IG9uIGJveHBsb3QKICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgIGZ1biA9IG1heCwKICAgICAgICBhZXMobGFiZWwgPSBzcHJpbnRmKCIlMS4yZiIsIGFmdGVyX3N0YXQoeSkpKSwKICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHkgPSAwLjAyNSksCiAgICAgICAgc2l6ZSA9IDMKICAgICkgKwogICAgbGFicygKICAgICAgICB4ID0gcGFzdGUwKCJQaHlzaWNpYW4gQWdyZWVkIFJlY29yZHMgKG49IiwgbmFncmVlLCAiLCAxMDAlKSIpLAogICAgICAgIHkgPSAiUENDQyAoMD1Mb3csIDE9SGlnaCkiCiAgICApICsKICAgIHlsaW0oMC4yNSwgMC45KSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIHRoZW1lKAogICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4odCA9IDE2LCBiID0gMTIsIGwgPSAxMiwgciA9IDEyKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSAxMCkpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSksCiAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoCiAgICAgICAgICAgIGFycm93ID0gZ3JpZDo6YXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJjbSIpLCBlbmRzID0gImJvdGgiKQogICAgICAgICksCiAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCkKICAgICkKCiMgU2F2ZSB0aGUgcGxvdAphZ2VwbG90Cmdnc2F2ZSgiLi4vbWFudXNjcmlwdC9maWctcGVyZi1hZ2Vncm91cC5wZGYiLCBwbG90ID0gYWdlcGxvdCwgZHBpID0gMzAwKQpgYGAKCiMjIyMgU2V4IFBlcmZvcm1hbmNlCgpEaXNwbGF5IGEgcGxvdCBmb3IgZWFjaCBhZ2UgZ3JvdXAgb2YgcGVyZm9ybWFuY2UgZm9yIHBoeXNpY2lhbiBhZ3JlZWQgcmVjb3JkcyBieSBzZXguCgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDIuNX0KZm9yIChhIGluIGMoImFkdWx0IiwgImNoaWxkIiwgIm5lbyIpKSB7CiAgICAKICAgICMgQ3JlYXRlIGxhYmVsIGFuZCByZWYgZm9yIGFnZSBncm91cAogICAgYXJvd3MgPC0gZGYgJT4lIGZpbHRlcihpc19hZ3JlZWQgPT0gVFJVRSAmIGFnZSA9PSBhKSAlPiUgbnJvdwogICAgYWxhYmVsIDwtIGlmIChhID09ICJuZW8iKSAiTmVvbmF0YWwiIGVsc2Ugc3RyX3RvX3RpdGxlKGEpCiAgICBhdGl0bGUgPC0gcGFzdGUwKCJQaHlzaWNpYW4gQWdyZWVkXG4iLCBhbGFiZWwsICIgUmVjb3Jkc1xuKG49IiwgYXJvd3MsICIsIDEwMCUpIikKICAgIGFyZWYgPC0gc3RyX3RvX3RpdGxlKGEpCiAgICAKICAgICMgR2V0IHVuaXF1ZSBzZXggY29sdW1ucwogICAgc2V4X2NvbCA8LSBvdXQgJT4lIHNlbGVjdChzdGFydHNfd2l0aChzcHJpbnRmKAogICAgICAgICAgICAiUENDQyAlcyBTZXggQWdyZWUgIiwKICAgICAgICAgICAgYXJlZgogICAgICAgICkpKSAlPiUKICAgICAgICBuYW1lcwogICAgCiAgICAjIENyZWF0ZSBzZXggd2l0aCBjb3VudHMKICAgIHNleF9yZW1hcCA8LSBsaXN0KCkKICAgIHNleF9jb3VudHMgPC0gbGlzdCgpCiAgICBmb3IgKHN4X2NvbCBpbiBzZXhfY29sKSB7CiAgICAgICAgCiAgICAgICAgIyBHZXQgc2V4IHdpdGhvdXQgcHJlZml4CiAgICAgICAgc3ggPC0gZ3N1YihzcHJpbnRmKCJQQ0NDICVzIFNleCBBZ3JlZSAiLCBhcmVmKSwgIiIsIHN4X2NvbCkKICAgICAgICAKICAgICAgICAjIENhbGMgbnVtIGFuZCBwZXJjIGNhc2VzIGZvciBhZ2UgcmFuZ2UKICAgICAgICBuc2V4IDwtIGRmICU+JSBmaWx0ZXIoc2V4ID09IHN4ICYgaXNfYWdyZWVkID09IFRSVUUgJiBhZ2UgPT0gYSkgJT4lIG5yb3cKICAgICAgICBwc2V4IDwtIChuc2V4IC8gYXJvd3MpICogMTAwCiAgICAgICAgcHNleF9sYWJlbCA8LSBpZiAocm91bmQocHNleCkgPj0gMSkgcm91bmQocHNleCkgZWxzZSAiPDEiCiAgICAgICAgCiAgICAgICAgIyBBZGQgbGFiZWwgZm9yIGFnZSByYW5nZXMKICAgICAgICBzZXhfcmVtYXBbW3N4XV0gPC0gcGFzdGUwKAogICAgICAgICAgICBzcHJpbnRmKCIlcyAlcyIsIGFsYWJlbCwgc3gpLAogICAgICAgICAgICAiXG4obj0iLCBuc2V4LCAiLCAiLCBwc2V4X2xhYmVsLCAiJSkiCiAgICAgICAgKQogICAgICAgIAogICAgICAgICMgU3RvcmUgYWdlIHJhbmdlIGNvdW50IGRhdGEKICAgICAgICBzZXhfY291bnRzW1tzeF1dIDwtIG5zZXgKICAgIH0KICAgIAogICAgIyBQcmVwYXJlIGJveHBsb3QgZGF0YQogICAgc3hkYXRhIDwtIG91dCAlPiUKICAgICAgICBzZWxlY3QoCiAgICAgICAgICAgIE1vZGVsLAogICAgICAgICAgICBzdGFydHNfd2l0aChzcHJpbnRmKCJQQ0NDICVzIFNleCBBZ3JlZSIsIGFyZWYpKQogICAgICAgICkgJT4lCiAgICAgICAgcmVuYW1lX2F0KAogICAgICAgICAgICAgdmFycygtTW9kZWwpLAogICAgICAgICAgICAgfmdzdWIoc3ByaW50ZigiUENDQyAlcyBTZXggQWdyZWUgIiwgYXJlZiksICIiLCAuKQogICAgICAgICkgJT4lCiAgICAgICAgZmlsdGVyKCFzdHJfZGV0ZWN0KE1vZGVsLCAiJiIpKSAlPiUgIyByZW1vdmUgbXVsdGkgbW9kZWxzCiAgICAgICAgcGl2b3RfbG9uZ2VyKCAjIHRyYW5zZm9ybSB0byBsb25nIGZvcm1hdAogICAgICAgICAgICBjb2xzID0gLU1vZGVsLAogICAgICAgICAgICBuYW1lc190byA9ICJTZXgiLAogICAgICAgICAgICB2YWx1ZXNfdG8gPSAiUENDQyIKICAgICAgICApICU+JQogICAgICAgIG11dGF0ZSggIyByZW5hbWUgY29kIHdpdGggY291bnRzCiAgICAgICAgICAgICJTZXgiID0gcmVjb2RlKFNleCwgISEhc2V4X3JlbWFwKQogICAgICAgICkgJT4lCiAgICAgICAgZ3JvdXBfYnkoU2V4KSAlPiUKICAgICAgICBtdXRhdGUoICMgTWFrZSB2YWx1ZXMgbGVzcyB0aGFuIDAgZXF1YWwgdG8gMAogICAgICAgICAgICBQQ0NDID0gaWZfZWxzZShQQ0NDIDw9IDAsIDAsIFBDQ0MpCiAgICAgICAgKSAlPiUKICAgICAgICBtdXRhdGUoICMgQWRkIG1pbiwgbWlkLCBhbmQgbWF4IG1vZGVsIG5hbWVzIHRvIGxhYmVsCiAgICAgICAgICAgICJQQ0NDIE1pbiIgPSBpZl9lbHNlKAogICAgICAgICAgICAgICAgUENDQyA8PSBtaW4oUENDQykgKyAwLjA1LAogICAgICAgICAgICAgICAgcGFzdGUwKAogICAgICAgICAgICAgICAgICAgIE1vZGVsLAogICAgICAgICAgICAgICAgICAgIGlmX2Vsc2Uocm91bmQoUENDQywgMikgIT0gcm91bmQobWluKFBDQ0MpLCAyKSwgcGFzdGUwKCIgKCIsIHJvdW5kKFBDQ0MsIDIpLCAiKSIpLCAiIikKICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICBOQQogICAgICAgICAgICApLAogICAgICAgICAgICAiUENDQyBNYXgiID0gaWZfZWxzZSgKICAgICAgICAgICAgICAgIFBDQ0MgPj0gbWF4KFBDQ0MpIC0gMC4wNSwKICAgICAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgICAgICBNb2RlbCwKICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKHJvdW5kKFBDQ0MsIDIpICE9IHJvdW5kKG1heChQQ0NDKSwgMiksIHBhc3RlMCgiICgiLCByb3VuZChQQ0NDLCAyKSwgIikiKSwgIiIpCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgTkEKICAgICAgICAgICAgKSwKICAgICAgICAgICAgIlBDQ0MgTWlkIiA9IGlmX2Vsc2UoCiAgICAgICAgICAgICAgICBpcy5uYShgUENDQyBNaW5gKSAmIGlzLm5hKGBQQ0NDIE1heGApLAogICAgICAgICAgICAgICAgcGFzdGUwKE1vZGVsLCAiXG4oIiwgcm91bmQoUENDQywgMiksICIpIiksCiAgICAgICAgICAgICAgICBOQQogICAgICAgICAgICApLAogICAgICAgICAgICAiUENDQyBNaWQgVmFsdWUiID0gaWZfZWxzZSgKICAgICAgICAgICAgICAgIGlzLm5hKGBQQ0NDIE1pbmApICYgaXMubmEoYFBDQ0MgTWF4YCksCiAgICAgICAgICAgICAgICBQQ0NDLAogICAgICAgICAgICAgICAgTkEKICAgICAgICAgICAgKSwKICAgICAgICAgICAgIlBDQ0MgTWluIiA9IGlmX2Vsc2UoICMgQ29tYmluZSBpbnRvIG9uZSByb3cgaWYgY2xvc2UgUENDQwogICAgICAgICAgICAgICAgYFBDQ0NgID09IG1pbihQQ0NDKSwKICAgICAgICAgICAgICAgIGlmX2Vsc2UoCiAgICAgICAgICAgICAgICAgICAgc3VtKCFpcy5uYShgUENDQyBNaW5gKSkgPiAxLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMChuYS5vbWl0KGBQQ0NDIE1pbmApLCBjb2xsYXBzZSA9ICJcbiIpLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgICAgICAgICAgc3RyX3JlcGxhY2UobmEub21pdChgUENDQyBNaW5gKSwgIlxccypcXCguKlxcKSIsICIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiXG4iCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICksCiAgICAgICAgICAgICJQQ0NDIE1heCIgPSBpZl9lbHNlKAogICAgICAgICAgICAgICAgYFBDQ0NgID09IG1heChQQ0NDKSwKICAgICAgICAgICAgICAgIGlmX2Vsc2UoCiAgICAgICAgICAgICAgICAgICAgc3VtKCFpcy5uYShgUENDQyBNYXhgKSkgPiAxLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMChuYS5vbWl0KGBQQ0NDIE1heGApLCBjb2xsYXBzZSA9ICJcbiIpLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMChzdHJfcmVwbGFjZShuYS5vbWl0KGBQQ0NDIE1heGApLCAiXFxzKlxcKC4qXFwpIiwgIiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICJcbiIpCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgTkEKICAgICAgICAgICAgKQogICAgICAgICkKICAgIAogICAgIyBDcmVhdGUgY29kIG9yZGVyIGJhc2VkIG9uIG1heCBwY2NjCiAgICBzeG9yZGVyIDwtIHN4ZGF0YSAlPiUKICAgICAgICBncm91cF9ieShTZXgpICU+JQogICAgICAgIHN1bW1hcmlzZSgiUENDQyBNYXggVmFsdWUiID0gbWF4KFBDQ0MsIG5hLnJtID0gVFJVRSkpICU+JQogICAgICAgIHNlbGVjdChTZXgsIGBQQ0NDIE1heCBWYWx1ZWApICU+JQogICAgICAgIGFycmFuZ2UoZGVzYyhgUENDQyBNYXggVmFsdWVgKSkgJT4lCiAgICAgICAgcHVsbChTZXgpCiAgICBzeGRhdGEkU2V4IDwtIGZhY3RvcihzeGRhdGEkU2V4LCBsZXZlbHMgPSByZXYoc3hvcmRlcikpCiAgICAKICAgICMgUGxvdCBib3hwbG90CiAgICBzeHBsb3QgPC0gc3hkYXRhICU+JQogICAgICAgIGdncGxvdChhZXMoeCA9IFNleCwgeSA9IFBDQ0MpKSArCiAgICAgICAgZ2VvbV9ib3hwbG90KAogICAgICAgICAgICBsaW5ld2lkdGggPSAwLjUsCiAgICAgICAgICAgIHdpZHRoID0gMC4yNQogICAgICAgICkgKwogICAgICAgIGdlb21fcG9pbnQoCiAgICAgICAgICAgIGFlcyh5ID0gYFBDQ0MgTWlkIFZhbHVlYCksCiAgICAgICAgICAgIHNoYXBlID0gMSwKICAgICAgICAgICAgc2l6ZSA9IDEuNSwKICAgICAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsCiAgICAgICAgICAgIGNvbG9yID0gImRhcmtncmF5IiwKICAgICAgICAgICAgYWxwaGEgPSAwLjgsCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeCA9IC0wLjM1KQogICAgICAgICkgKwogICAgICAgIGdlb21fdGV4dCgKICAgICAgICAgICAgYWVzKGxhYmVsID0gYFBDQ0MgTWluYCksCiAgICAgICAgICAgIGNvbG9yID0gIiM0ZDRkNGQiLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICBoanVzdCA9IDEsCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeSA9IC0wLjA5KQogICAgICAgICkgKwogICAgICAgIGdlb21fdGV4dCgKICAgICAgICAgICAgYWVzKGxhYmVsID0gYFBDQ0MgTWlkYCksCiAgICAgICAgICAgIGNvbG9yID0gIiM0ZDRkNGQiLAogICAgICAgICAgICBzaXplID0gMiwKICAgICAgICAgICAgaGp1c3QgPSAxLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHkgPSAtMC4wMjUsIHggPSAtMC4zNSkKICAgICAgICApICsKICAgICAgICBnZW9tX3RleHQoCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IGBQQ0NDIE1heGApLAogICAgICAgICAgICBjb2xvciA9ICIjNGQ0ZDRkIiwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgaGp1c3QgPSAwLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHkgPSAwLjA5KQogICAgICAgICkgKwogICAgICAgIHN0YXRfc3VtbWFyeSggIyBtaW4gcGNjYyB0eHQgb24gYm94cGxvdAogICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgICBmdW4gPSBtaW4sCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IHNwcmludGYoIiUxLjJmIiwgYWZ0ZXJfc3RhdCh5KSkpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHkgPSAtMC4wNCksCiAgICAgICAgICAgIHNpemUgPSAzCiAgICAgICAgKSArCiAgICAgICAgc3RhdF9zdW1tYXJ5KCAjIG1heCBwY2NjIHR4dCBvbiBib3hwbG90CiAgICAgICAgICAgIGdlb20gPSAidGV4dCIsCiAgICAgICAgICAgIGZ1biA9IG1heCwKICAgICAgICAgICAgYWVzKGxhYmVsID0gc3ByaW50ZigiJTEuMmYiLCBhZnRlcl9zdGF0KHkpKSksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeSA9IDAuMDQpLAogICAgICAgICAgICBzaXplID0gMwogICAgICAgICkgKwogICAgICAgIGxhYnMoCiAgICAgICAgICAgIHggPSBhdGl0bGUsCiAgICAgICAgICAgIHkgPSAiUENDQyAoMD1Mb3csIDE9SGlnaCkiCiAgICAgICAgKSArCiAgICAgICAgeWxpbSgwLjEsIDEpICsKICAgICAgICBjb29yZF9mbGlwKCkgKwogICAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgICAgdGhlbWUoCiAgICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksCiAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSksCiAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKAogICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgICAgYXJyb3cgPSBncmlkOjphcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImNtIiksIGVuZHMgPSAiYm90aCIpCiAgICAgICAgICAgICkKICAgICAgICApCiAgICAKICAgICMgU2F2ZSB0aGUgcGxvdAogICAgcHJpbnQoc3hwbG90KQogICAgZ2dzYXZlKAogICAgICAgIHNwcmludGYoIi4uL21hbnVzY3JpcHQvZmlnLXBlcmYtc2V4LSVzLnBkZiIsIGEpLAogICAgICAgIHBsb3QgPSBzeHBsb3QsCiAgICAgICAgZHBpID0gMzAwCiAgICApCn0KYGBgCgojIyMjIEFnZSBSYW5nZSBQZXJmb3JtYW5jZQoKRGlzcGxheSBhIHBsb3QgZm9yIGVhY2ggYWdlIGdyb3VwIG9mIHBlcmZvcm1hbmNlIGZvciBwaHlzaWNpYW4gYWdyZWVkIHJlY29yZHMgYnkgYWdlIHJhbmdlcy4KCmBgYHtyfQpmb3IgKGEgaW4gYygiYWR1bHQiLCAiY2hpbGQiLCAibmVvIikpIHsKICAgIAogICAgIyBDcmVhdGUgbGFiZWwgYW5kIHJlZiBmb3IgYWdlIGdyb3VwCiAgICBhcm93cyA8LSBkZiAlPiUgZmlsdGVyKGlzX2FncmVlZCA9PSBUUlVFICYgYWdlID09IGEpICU+JSBucm93CiAgICBhbGFiZWwgPC0gaWYgKGEgPT0gIm5lbyIpICJOZW9uYXRhbCIgZWxzZSBzdHJfdG9fdGl0bGUoYSkKICAgIGF0aXRsZSA8LSBpZiAoYSA9PSAibmVvIikgewogICAgICAgIHBhc3RlMCgiUGh5c2ljaWFuIEFncmVlZFxuIiwgYWxhYmVsLCAiIFJlY29yZHNcbihuPSIsIGFyb3dzLCAiLCAxMDAlKSIpCiAgICB9IGVsc2UgewogICAgICAgIHBhc3RlMCgiUGh5c2ljaWFuIEFncmVlZCAiLCBhbGFiZWwsICIgUmVjb3JkcyAobj0iLCBhcm93cywgIiwgMTAwJSkiKQogICAgfQogICAgYXJlZiA8LSBzdHJfdG9fdGl0bGUoYSkKICAgIAogICAgIyBHZXQgdW5pcXVlIGFnZSByYW5nZSBjb2x1bW5zCiAgICBhcmFuZ2VfY29sIDwtIG91dCAlPiUgc2VsZWN0KHN0YXJ0c193aXRoKHNwcmludGYoCiAgICAgICAgICAgICJQQ0NDICVzIEFnZSBBZ3JlZSAiLAogICAgICAgICAgICBhcmVmCiAgICAgICAgKSkpICU+JQogICAgICAgIG5hbWVzCiAgICAKICAgICMgQ3JlYXRlIGFnZSByYW5nZSB3aXRoIGNvdW50cwogICAgYXJhbmdlX3JlbWFwIDwtIGxpc3QoKQogICAgYXJhbmdlX2NvdW50cyA8LSBsaXN0KCkKICAgIGZvciAoYXJfY29sIGluIGFyYW5nZV9jb2wpIHsKICAgICAgICAKICAgICAgICAjIEdldCBjb2Qgd2l0aG91dCBwcmVmaXgKICAgICAgICBhciA8LSBnc3ViKHNwcmludGYoIlBDQ0MgJXMgQWdlIEFncmVlICIsIGFyZWYpLCAiIiwgYXJfY29sKQogICAgICAgIAogICAgICAgICMgQ2FsYyBudW0gYW5kIHBlcmMgY2FzZXMgZm9yIGFnZSByYW5nZQogICAgICAgIG5hcmFuZ2UgPC0gZGYgJT4lIGZpbHRlcihhZ2VfcmFuZ2UgPT0gYXIgJiBpc19hZ3JlZWQgPT0gVFJVRSAmIGFnZSA9PSBhKSAlPiUgbnJvdwogICAgICAgIHBhcmFuZ2UgPC0gKG5hcmFuZ2UgLyBhcm93cykgKiAxMDAKICAgICAgICBwYXJhbmdlX2xhYmVsIDwtIGlmIChyb3VuZChwYXJhbmdlKSA+PSAxKSByb3VuZChwYXJhbmdlKSBlbHNlICI8MSIKICAgICAgICAKICAgICAgICAjIEFkZCBsYWJlbCBmb3IgYWdlIHJhbmdlcwogICAgICAgIGFyYW5nZV9yZW1hcFtbYXJdXSA8LSBwYXN0ZTAoCiAgICAgICAgICAgIGFyLAogICAgICAgICAgICAiXG4obj0iLCBuYXJhbmdlLCAiLCAiLCBwYXJhbmdlX2xhYmVsLCAiJSkiCiAgICAgICAgKQogICAgICAgIAogICAgICAgICMgU3RvcmUgYWdlIHJhbmdlIGNvdW50IGRhdGEKICAgICAgICBhcmFuZ2VfY291bnRzW1thcl1dIDwtIG5hcmFuZ2UKICAgIH0KICAgIAogICAgIyBQcmVwYXJlIGJveHBsb3QgZGF0YQogICAgYXJkYXRhIDwtIG91dCAlPiUKICAgICAgICBzZWxlY3QoCiAgICAgICAgICAgIE1vZGVsLAogICAgICAgICAgICBzdGFydHNfd2l0aChzcHJpbnRmKCJQQ0NDICVzIEFnZSBBZ3JlZSIsIGFyZWYpKQogICAgICAgICkgJT4lCiAgICAgICAgcmVuYW1lX2F0KAogICAgICAgICAgICAgdmFycygtTW9kZWwpLAogICAgICAgICAgICAgfmdzdWIoc3ByaW50ZigiUENDQyAlcyBBZ2UgQWdyZWUgIiwgYXJlZiksICIiLCAuKQogICAgICAgICkgJT4lCiAgICAgICAgZmlsdGVyKCFzdHJfZGV0ZWN0KE1vZGVsLCAiJiIpKSAlPiUgIyByZW1vdmUgbXVsdGkgbW9kZWxzCiAgICAgICAgcGl2b3RfbG9uZ2VyKCAjIHRyYW5zZm9ybSB0byBsb25nIGZvcm1hdAogICAgICAgICAgICBjb2xzID0gLU1vZGVsLAogICAgICAgICAgICBuYW1lc190byA9ICJBZ2UgUmFuZ2UiLAogICAgICAgICAgICB2YWx1ZXNfdG8gPSAiUENDQyIKICAgICAgICApICU+JQogICAgICAgIG11dGF0ZSggIyByZW5hbWUgY29kIHdpdGggY291bnRzCiAgICAgICAgICAgICJBZ2UgUmFuZ2UiID0gcmVjb2RlKGBBZ2UgUmFuZ2VgLCAhISFhcmFuZ2VfcmVtYXApCiAgICAgICAgKSAlPiUKICAgICAgICBncm91cF9ieShgQWdlIFJhbmdlYCkgJT4lCiAgICAgICAgbXV0YXRlKCAjIE1ha2UgdmFsdWVzIGxlc3MgdGhhbiAwIGVxdWFsIHRvIDAKICAgICAgICAgICAgUENDQyA9IGlmX2Vsc2UoUENDQyA8PSAwLCAwLCBQQ0NDKQogICAgICAgICkgJT4lCiAgICAgICAgbXV0YXRlKCAjIEFkZCBtaW4sIG1pZCwgYW5kIG1heCBtb2RlbCBuYW1lcyB0byBsYWJlbAogICAgICAgICAgICAiUENDQyBNaW4iID0gaWZfZWxzZSgKICAgICAgICAgICAgICAgIFBDQ0MgPD0gbWluKFBDQ0MpICsgMC4wNSwKICAgICAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgICAgICBNb2RlbCwKICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKHJvdW5kKFBDQ0MsIDIpICE9IHJvdW5kKG1pbihQQ0NDKSwgMiksIHBhc3RlMCgiICgiLCByb3VuZChQQ0NDLCAyKSwgIikiKSwgIiIpCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgTkEKICAgICAgICAgICAgKSwKICAgICAgICAgICAgIlBDQ0MgTWF4IiA9IGlmX2Vsc2UoCiAgICAgICAgICAgICAgICBQQ0NDID49IG1heChQQ0NDKSAtIDAuMDUsCiAgICAgICAgICAgICAgICBwYXN0ZTAoCiAgICAgICAgICAgICAgICAgICAgTW9kZWwsCiAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShyb3VuZChQQ0NDLCAyKSAhPSByb3VuZChtYXgoUENDQyksIDIpLCBwYXN0ZTAoIiAoIiwgcm91bmQoUENDQywgMiksICIpIiksICIiKQogICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICksCiAgICAgICAgICAgICJQQ0NDIE1pZCIgPSBpZl9lbHNlKAogICAgICAgICAgICAgICAgaXMubmEoYFBDQ0MgTWluYCkgJiBpcy5uYShgUENDQyBNYXhgKSwKICAgICAgICAgICAgICAgIHBhc3RlMChNb2RlbCwgIlxuKCIsIHJvdW5kKFBDQ0MsIDIpLCAiKSIpLAogICAgICAgICAgICAgICAgTkEKICAgICAgICAgICAgKSwKICAgICAgICAgICAgIlBDQ0MgTWluIiA9IGlmX2Vsc2UoICMgQ29tYmluZSBpbnRvIG9uZSByb3cgaWYgY2xvc2UgUENDQwogICAgICAgICAgICAgICAgYFBDQ0NgID09IG1pbihQQ0NDKSwKICAgICAgICAgICAgICAgIGlmX2Vsc2UoCiAgICAgICAgICAgICAgICAgICAgc3VtKCFpcy5uYShgUENDQyBNaW5gKSkgPiAxLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMChuYS5vbWl0KGBQQ0NDIE1pbmApLCBjb2xsYXBzZSA9ICJcbiIpLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgICAgICAgICAgc3RyX3JlcGxhY2UobmEub21pdChgUENDQyBNaW5gKSwgIlxccypcXCguKlxcKSIsICIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiXG4iCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICksCiAgICAgICAgICAgICJQQ0NDIE1heCIgPSBpZl9lbHNlKAogICAgICAgICAgICAgICAgYFBDQ0NgID09IG1heChQQ0NDKSwKICAgICAgICAgICAgICAgIGlmX2Vsc2UoCiAgICAgICAgICAgICAgICAgICAgc3VtKCFpcy5uYShgUENDQyBNYXhgKSkgPiAxLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMChuYS5vbWl0KGBQQ0NDIE1heGApLCBjb2xsYXBzZSA9ICJcbiIpLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMChzdHJfcmVwbGFjZShuYS5vbWl0KGBQQ0NDIE1heGApLCAiXFxzKlxcKC4qXFwpIiwgIiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICJcbiIpCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgTkEKICAgICAgICAgICAgKQogICAgICAgICkKICAgIAogICAgIyBDcmVhdGUgY29kIG9yZGVyIGJhc2VkIG9uIG1heCBwY2NjCiAgICBhcm9yZGVyIDwtIGFyZGF0YSAlPiUKICAgICAgICBncm91cF9ieShgQWdlIFJhbmdlYCkgJT4lCiAgICAgICAgc3VtbWFyaXNlKCJQQ0NDIE1heCBWYWx1ZSIgPSBtYXgoUENDQywgbmEucm0gPSBUUlVFKSkgJT4lCiAgICAgICAgc2VsZWN0KGBBZ2UgUmFuZ2VgLCBgUENDQyBNYXggVmFsdWVgKSAlPiUKICAgICAgICBhcnJhbmdlKGRlc2MoYFBDQ0MgTWF4IFZhbHVlYCkpICU+JQogICAgICAgIHB1bGwoYEFnZSBSYW5nZWApCiAgICBhcmRhdGEkYEFnZSBSYW5nZWAgPC0gZmFjdG9yKGFyZGF0YSRgQWdlIFJhbmdlYCwgbGV2ZWxzID0gcmV2KGFyb3JkZXIpKQogICAgCiAgICAjIFBsb3QgYm94cGxvdAogICAgYXJwbG90IDwtIGFyZGF0YSAlPiUKICAgICAgICBnZ3Bsb3QoYWVzKHggPSBgQWdlIFJhbmdlYCwgeSA9IFBDQ0MpKSArCiAgICAgICAgZ2VvbV9ib3hwbG90KAogICAgICAgICAgICBsaW5ld2lkdGggPSAwLjUsCiAgICAgICAgICAgIHdpZHRoID0gMC4yNQogICAgICAgICkgKwogICAgICAgIGdlb21fdGV4dCgKICAgICAgICAgICAgYWVzKGxhYmVsID0gYFBDQ0MgTWluYCksCiAgICAgICAgICAgIGNvbG9yID0gIiM0ZDRkNGQiLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICBoanVzdCA9IDEsCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeSA9IC0wLjA4KQogICAgICAgICkgKwogICAgICAgIGdlb21fdGV4dCgKICAgICAgICAgICAgYWVzKGxhYmVsID0gYFBDQ0MgTWF4YCksCiAgICAgICAgICAgIGNvbG9yID0gIiM0ZDRkNGQiLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICBoanVzdCA9IDAsCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeSA9IDAuMDgpCiAgICAgICAgKSArCiAgICAgICAgc3RhdF9zdW1tYXJ5KCAjIG1pbiBwY2NjIHR4dCBvbiBib3hwbG90CiAgICAgICAgICAgIGdlb20gPSAidGV4dCIsCiAgICAgICAgICAgIGZ1biA9IG1pbiwKICAgICAgICAgICAgYWVzKGxhYmVsID0gc3ByaW50ZigiJTEuMmYiLCBhZnRlcl9zdGF0KHkpKSksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeSA9IC0wLjA0KSwKICAgICAgICAgICAgc2l6ZSA9IDMKICAgICAgICApICsKICAgICAgICBzdGF0X3N1bW1hcnkoICMgbWF4IHBjY2MgdHh0IG9uIGJveHBsb3QKICAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICAgZnVuID0gbWF4LAogICAgICAgICAgICBhZXMobGFiZWwgPSBzcHJpbnRmKCIlMS4yZiIsIGFmdGVyX3N0YXQoeSkpKSwKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh5ID0gMC4wNCksCiAgICAgICAgICAgIHNpemUgPSAzCiAgICAgICAgKSArCiAgICAgICAgbGFicygKICAgICAgICAgICAgeCA9IGF0aXRsZSwKICAgICAgICAgICAgeSA9ICJQQ0NDICgwPUxvdywgMT1IaWdoKSIKICAgICAgICApICsKICAgICAgICB5bGltKDAuMSwgMSkgKwogICAgICAgIGNvb3JkX2ZsaXAoKSArCiAgICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgICB0aGVtZSgKICAgICAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gMTApKSwKICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApKSwKICAgICAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoCiAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgICBhcnJvdyA9IGdyaWQ6OmFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKSwgZW5kcyA9ICJib3RoIikKICAgICAgICAgICAgKQogICAgICAgICkKICAgIAogICAgIyBTYXZlIHRoZSBwbG90CiAgICBwcmludChhcnBsb3QpCiAgICBnZ3NhdmUoCiAgICAgICAgc3ByaW50ZigiLi4vbWFudXNjcmlwdC9maWctcGVyZi1hZ2VyYW5nZS0lcy5wZGYiLCBhKSwKICAgICAgICBwbG90ID0gYXJwbG90LAogICAgICAgIGRwaSA9IDMwMCwKICAgICAgICB3aWR0aCA9IGlmIChhID09ICJhZHVsdCIpIDYgZWxzZSBpZiAoYSA9PSAibmVvIikgNiBlbHNlIE5BLAogICAgICAgIGhlaWdodCA9IGlmIChhID09ICJhZHVsdCIpIDggZWxzZSBpZiAoYSA9PSAibmVvIikgMi41IGVsc2UgTkEKICAgICkKfQpgYGAKCiMjIyMgQ09EIFBlcmZvcm1hbmNlCgpQbG90IG1vZGVsIHBlcmZvcm1hbmNlIGZvciBwaHlzaWNpYW4gYWdyZWVkIHJlY29yZHMgYnkgY2F1c2Ugb2YgZGVhdGggY2F0ZWdvcnkuCgpgYGB7cn0KZm9yIChhIGluIGMoImFkdWx0IiwgImNoaWxkIiwgIm5lbyIpKSB7CiAgICAKICAgICMgQ3JlYXRlIGxhYmVsIGZvciBhZ2UgZ3JvdXAKICAgIGFyb3dzIDwtIGRmICU+JSBmaWx0ZXIoaXNfYWdyZWVkID09IFRSVUUgJiBhZ2UgPT0gYSkgJT4lIG5yb3cKICAgIGFsYWJlbCA8LSBpZiAoYSA9PSAibmVvIikgIk5lb25hdGFsIiBlbHNlIHN0cl90b190aXRsZShhKQogICAgYXRpdGxlIDwtIHBhc3RlMCgiUGh5c2ljaWFuIEFncmVlZCAiLCBhbGFiZWwsICIgUmVjb3JkcyAobj0iLCBhcm93cywgIiwgMTAwJSkiKQogICAgYXJlZiA8LSBzdHJfdG9fdGl0bGUoYSkKICAgIAogICAgIyBHZXQgdW5pcXVlIGNhdXNlIGNvbHVtbnMKICAgIGNhdXNlc19jb2wgPC0gb3V0ICU+JSBzZWxlY3Qoc3RhcnRzX3dpdGgoc3ByaW50ZigKICAgICAgICAiUENDQyAlcyBDT0QgQWdyZWUgIiwgYXJlZgogICAgKSkpICU+JSBuYW1lcwogICAgCiAgICAjIENyZWF0ZSBjb2Qgd2l0aCBjb3VudHMKICAgIGNvZF9yZW1hcCA8LSBsaXN0KCkKICAgIGNvZF9jb3VudHMgPC0gbGlzdCgpCiAgICBmb3IgKGNvZF9jb2wgaW4gY2F1c2VzX2NvbCkgewogICAgICAgIAogICAgICAgICMgR2V0IGNvZCB3aXRob3V0IHByZWZpeAogICAgICAgIGNvZCA8LSBnc3ViKHNwcmludGYoIlBDQ0MgJXMgQ09EIEFncmVlICIsIGFyZWYpLCAiIiwgY29kX2NvbCkKICAgICAgICAKICAgICAgICAjIENhbGN1bGF0ZSBudW0gYW5kIHBlcmMgY2FzZXMgZm9yIGNvZAogICAgICAgIG5jb2QgPC0gZGYgJT4lIGZpbHRlcihwaHlzaWNpYW5fY2docjEwID09IGNvZCAmIGlzX2FncmVlZCA9PSBUUlVFICYgYWdlID09IGEpICU+JSBucm93CiAgICAgICAgcGNvZCA8LSAobmNvZCAvIG5hZ3JlZSkgKiAxMDAKICAgICAgICBwY29kX2xhYmVsIDwtIGlmIChyb3VuZChwY29kKSA+PSAxKSByb3VuZChwY29kKSBlbHNlICI8MSIKICAgICAgICAKICAgICAgICAjIEJyZWFrIGNvZCBpbnRvIG5ld2xpbmVzIGlmIG1vcmUgdGhhbiAzIHdvcmRzCiAgICAgICAgaWYgKHN0cl9jb3VudChjb2QsICJcXHMrIikgPiAzKSB7CiAgICAgICAgICAgIGNvZF9sYWJlbCA8LSBzdHJfcmVwbGFjZShjb2QsICIoXFxTK1xccytcXFMrXFxzK1xcUyspICIsICJcXDFcbiIpCiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgY29kX2xhYmVsIDwtIGNvZAogICAgICAgIH0KICAgICAgICAKICAgICAgICAjIEFkZCBsYWJlbCBmb3IgY29kCiAgICAgICAgY29kX3JlbWFwW1tjb2RdXSA8LSBwYXN0ZTAoY29kX2xhYmVsLCAiXG4obj0iLCBuY29kLCAiLCAiLCBwY29kX2xhYmVsLCAiJSkiKQogICAgICAgIAogICAgICAgICMgU3RvcmUgY29kIGNvdW50IGRhdGEKICAgICAgICBjb2RfY291bnRzW1tjb2RdXSA8LSBuY29kCiAgICB9CiAgICAKICAgICMgUHJlcGFyZSBib3hwbG90IGRhdGEKICAgIGNvZGRhdGEgPC0gb3V0ICU+JQogICAgICAgIHNlbGVjdCgKICAgICAgICAgICAgTW9kZWwsCiAgICAgICAgICAgIHN0YXJ0c193aXRoKHNwcmludGYoIlBDQ0MgJXMgQ09EIEFncmVlICIsIGFyZWYpKQogICAgICAgICkgJT4lCiAgICAgICAgcmVuYW1lX2F0KAogICAgICAgICAgICAgdmFycygtTW9kZWwpLAogICAgICAgICAgICAgfmdzdWIoc3ByaW50ZigiUENDQyAlcyBDT0QgQWdyZWUgIiwgYXJlZiksICIiLCAuKQogICAgICAgICkgJT4lCiAgICAgICAgZmlsdGVyKCFzdHJfZGV0ZWN0KE1vZGVsLCAiJiIpKSAlPiUgIyByZW1vdmUgbXVsdGkgbW9kZWxzCiAgICAgICAgcGl2b3RfbG9uZ2VyKCAjIHRyYW5zZm9ybSB0byBsb25nIGZvcm1hdAogICAgICAgICAgICBjb2xzID0gLU1vZGVsLAogICAgICAgICAgICBuYW1lc190byA9ICJDT0QiLAogICAgICAgICAgICB2YWx1ZXNfdG8gPSAiUENDQyIKICAgICAgICApICU+JQogICAgICAgIG11dGF0ZSggIyByZW5hbWUgY29kIHdpdGggY291bnRzCiAgICAgICAgICAgICJDT0QiID0gcmVjb2RlKGBDT0RgLCAhISFjb2RfcmVtYXApCiAgICAgICAgKSAlPiUKICAgICAgICBncm91cF9ieShgQ09EYCkgJT4lCiAgICAgICAgbXV0YXRlKCAjIE1ha2UgdmFsdWVzIGxlc3MgdGhhbiAwIGVxdWFsIHRvIDAKICAgICAgICAgICAgUENDQyA9IGlmX2Vsc2UoUENDQyA8PSAwLCAwLCBQQ0NDKQogICAgICAgICkgJT4lCiAgICAgICAgbXV0YXRlKCAjIEFkZCBtaW4sIG1pZCwgYW5kIG1heCBtb2RlbCBuYW1lcyB0byBsYWJlbAogICAgICAgICAgICAiUENDQyBNaW4iID0gaWZfZWxzZSgKICAgICAgICAgICAgICAgIFBDQ0MgPD0gbWluKFBDQ0MpICsgMC4wNSwKICAgICAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgICAgICBNb2RlbCwKICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKHJvdW5kKFBDQ0MsIDIpICE9IHJvdW5kKG1pbihQQ0NDKSwgMiksIHBhc3RlMCgiICgiLCByb3VuZChQQ0NDLCAyKSwgIikiKSwgIiIpCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgTkEKICAgICAgICAgICAgKSwKICAgICAgICAgICAgIlBDQ0MgTWF4IiA9IGlmX2Vsc2UoCiAgICAgICAgICAgICAgICBQQ0NDID49IG1heChQQ0NDKSAtIDAuMDUsCiAgICAgICAgICAgICAgICBwYXN0ZTAoCiAgICAgICAgICAgICAgICAgICAgTW9kZWwsCiAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShyb3VuZChQQ0NDLCAyKSAhPSByb3VuZChtYXgoUENDQyksIDIpLCBwYXN0ZTAoIiAoIiwgcm91bmQoUENDQywgMiksICIpIiksICIiKQogICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICksCiAgICAgICAgICAgICJQQ0NDIE1pZCIgPSBpZl9lbHNlKAogICAgICAgICAgICAgICAgaXMubmEoYFBDQ0MgTWluYCkgJiBpcy5uYShgUENDQyBNYXhgKSwKICAgICAgICAgICAgICAgIHBhc3RlMChNb2RlbCwgIlxuKCIsIHJvdW5kKFBDQ0MsIDIpLCAiKSIpLAogICAgICAgICAgICAgICAgTkEKICAgICAgICAgICAgKSwKICAgICAgICAgICAgIlBDQ0MgTWluIiA9IGlmX2Vsc2UoICMgQ29tYmluZSBpbnRvIG9uZSByb3cgaWYgY2xvc2UgUENDQwogICAgICAgICAgICAgICAgYFBDQ0NgID09IG1pbihQQ0NDKSwKICAgICAgICAgICAgICAgIGlmX2Vsc2UoCiAgICAgICAgICAgICAgICAgICAgc3VtKCFpcy5uYShgUENDQyBNaW5gKSkgPiAxLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMChuYS5vbWl0KGBQQ0NDIE1pbmApLCBjb2xsYXBzZSA9ICJcbiIpLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgICAgICAgICAgc3RyX3JlcGxhY2UobmEub21pdChgUENDQyBNaW5gKSwgIlxccypcXCguKlxcKSIsICIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiXG4iCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICksCiAgICAgICAgICAgICJQQ0NDIE1heCIgPSBpZl9lbHNlKAogICAgICAgICAgICAgICAgYFBDQ0NgID09IG1heChQQ0NDKSwKICAgICAgICAgICAgICAgIGlmX2Vsc2UoCiAgICAgICAgICAgICAgICAgICAgc3VtKCFpcy5uYShgUENDQyBNYXhgKSkgPiAxLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMChuYS5vbWl0KGBQQ0NDIE1heGApLCBjb2xsYXBzZSA9ICJcbiIpLAogICAgICAgICAgICAgICAgICAgIHBhc3RlMChzdHJfcmVwbGFjZShuYS5vbWl0KGBQQ0NDIE1heGApLCAiXFxzKlxcKC4qXFwpIiwgIiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICJcbiIpCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgTkEKICAgICAgICAgICAgKQogICAgICAgICkKICAgIAogICAgIyBDcmVhdGUgY29kIG9yZGVyIGJhc2VkIG9uIG1heCBwY2NjCiAgICBjb2RvcmRlciA8LSBjb2RkYXRhICU+JQogICAgICAgIGdyb3VwX2J5KENPRCkgJT4lCiAgICAgICAgc3VtbWFyaXNlKCJQQ0NDIE1heCBWYWx1ZSIgPSBtYXgoUENDQywgbmEucm0gPSBUUlVFKSkgJT4lCiAgICAgICAgc2VsZWN0KENPRCwgYFBDQ0MgTWF4IFZhbHVlYCkgJT4lCiAgICAgICAgYXJyYW5nZShkZXNjKGBQQ0NDIE1heCBWYWx1ZWApKSAlPiUKICAgICAgICBwdWxsKENPRCkKICAgIGNvZGRhdGEkQ09EIDwtIGZhY3Rvcihjb2RkYXRhJENPRCwgbGV2ZWxzID0gcmV2KGNvZG9yZGVyKSkKICAgIAogICAgIyBQbG90IGJveHBsb3QKICAgIGNvZHBsb3QgPC0gY29kZGF0YSAlPiUKICAgICAgICBnZ3Bsb3QoYWVzKHggPSBDT0QsIHkgPSBQQ0NDKSkgKwogICAgICAgIGdlb21fYm94cGxvdCgKICAgICAgICAgICAgbGluZXdpZHRoID0gMC41LAogICAgICAgICAgICB3aWR0aCA9IDAuMjUKICAgICAgICApICsKICAgICAgICBnZW9tX3RleHQoCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IGBQQ0NDIE1pbmApLAogICAgICAgICAgICBjb2xvciA9ICIjNGQ0ZDRkIiwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgaGp1c3QgPSAxLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHkgPSAtMC4xMikKICAgICAgICApICsKICAgICAgICBnZW9tX3RleHQoCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IGBQQ0NDIE1heGApLAogICAgICAgICAgICBjb2xvciA9ICIjNGQ0ZDRkIiwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgaGp1c3QgPSAwLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHkgPSAwLjEyKQogICAgICAgICkgKwogICAgICAgIHN0YXRfc3VtbWFyeSggIyBtaW4gcGNjYyB0eHQgb24gYm94cGxvdAogICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgICBmdW4gPSBtaW4sCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IHNwcmludGYoIiUxLjJmIiwgYWZ0ZXJfc3RhdCh5KSkpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHkgPSAtMC4wNiksCiAgICAgICAgICAgIHNpemUgPSAzCiAgICAgICAgKSArCiAgICAgICAgc3RhdF9zdW1tYXJ5KCAjIG1heCBwY2NjIHR4dCBvbiBib3hwbG90CiAgICAgICAgICAgIGdlb20gPSAidGV4dCIsCiAgICAgICAgICAgIGZ1biA9IG1heCwKICAgICAgICAgICAgYWVzKGxhYmVsID0gc3ByaW50ZigiJTEuMmYiLCBhZnRlcl9zdGF0KHkpKSksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeSA9IDAuMDYpLAogICAgICAgICAgICBzaXplID0gMwogICAgICAgICkgKwogICAgICAgIGxhYnMoCiAgICAgICAgICAgIHggPSBhdGl0bGUsCiAgICAgICAgICAgIHkgPSAiUENDQyAoMD1Mb3csIDE9SGlnaCkiCiAgICAgICAgKSArCiAgICAgICAgeWxpbSgtMC4zLCAxLjMpICsKICAgICAgICBjb29yZF9mbGlwKCkgKwogICAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgICAgdGhlbWUoCiAgICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSksCiAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSksCiAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKAogICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgICAgYXJyb3cgPSBncmlkOjphcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImNtIiksIGVuZHMgPSAiYm90aCIpCiAgICAgICAgICAgICkKICAgICAgICApCiAgICAKICAgICMgU2F2ZSB0aGUgcGxvdAogICAgcHJpbnQoY29kcGxvdCkKICAgIGdnc2F2ZSgKICAgICAgICBzcHJpbnRmKCIuLi9tYW51c2NyaXB0L2ZpZy1wZXJmLWNvZC0lcy5wZGYiLCBhKSwKICAgICAgICBwbG90ID0gY29kcGxvdCwKICAgICAgICBkcGkgPSAzMDAsCiAgICAgICAgd2lkdGggPSBpZiAoYSA9PSAiYWR1bHQiKSA4IGVsc2UgTkEsCiAgICAgICAgaGVpZ2h0ID0gaWYgKGEgPT0gImFkdWx0IikgMTAgZWxzZSBOQQogICAgKQp9CmBgYA==